Commit | Line | Data |
---|---|---|
ff1f3280 SC |
1 | /* |
2 | * gfio - gui front end for fio - the flexible io tester | |
3 | * | |
4 | * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> | |
c0187f3b | 5 | * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk> |
ff1f3280 SC |
6 | * |
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 | */ | |
8232e285 | 24 | #include <locale.h> |
60f6b330 | 25 | #include <malloc.h> |
6b79c80c | 26 | #include <string.h> |
8232e285 | 27 | |
5b7573ab | 28 | #include <glib.h> |
2fd3bb0e | 29 | #include <cairo.h> |
ff1f3280 SC |
30 | #include <gtk/gtk.h> |
31 | ||
8232e285 | 32 | #include "fio.h" |
2fd3bb0e | 33 | #include "graph.h" |
8232e285 | 34 | |
38634cb1 JA |
35 | #define GFIO_MIME "text/fio" |
36 | ||
63a130b7 | 37 | static int gfio_server_running; |
f3e8440f | 38 | static const char *gfio_graph_font; |
8577f4fd | 39 | static unsigned int gfio_graph_limit = 100; |
814479d5 | 40 | static GdkColor white; |
63a130b7 | 41 | |
6b79c80c | 42 | static void view_log(GtkWidget *w, gpointer data); |
3e47bd25 | 43 | |
f3074008 SC |
44 | #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) |
45 | ||
46 | typedef void (*clickfunction)(GtkWidget *widget, gpointer data); | |
47 | ||
3e47bd25 | 48 | static void connect_clicked(GtkWidget *widget, gpointer data); |
f3074008 | 49 | static void start_job_clicked(GtkWidget *widget, gpointer data); |
b9d2f30a | 50 | static void send_clicked(GtkWidget *widget, gpointer data); |
f3074008 SC |
51 | |
52 | static struct button_spec { | |
53 | const char *buttontext; | |
54 | clickfunction f; | |
014f4024 JA |
55 | const char *tooltiptext[2]; |
56 | const int start_sensitive; | |
f3074008 | 57 | } buttonspeclist[] = { |
3e47bd25 | 58 | #define CONNECT_BUTTON 0 |
b9d2f30a JA |
59 | #define SEND_BUTTON 1 |
60 | #define START_JOB_BUTTON 2 | |
014f4024 JA |
61 | { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 }, |
62 | { "Send", send_clicked, { "Send job description to host", NULL }, 0 }, | |
b9d2f30a | 63 | { "Start Job", start_job_clicked, |
014f4024 | 64 | { "Start the current job on the server", NULL }, 0 }, |
f3074008 SC |
65 | }; |
66 | ||
843ad237 JA |
67 | struct probe_widget { |
68 | GtkWidget *hostname; | |
69 | GtkWidget *os; | |
70 | GtkWidget *arch; | |
71 | GtkWidget *fio_ver; | |
72 | }; | |
73 | ||
c80b74b0 JA |
74 | struct multitext_widget { |
75 | GtkWidget *entry; | |
76 | char **text; | |
77 | unsigned int cur_text; | |
78 | unsigned int max_text; | |
79 | }; | |
80 | ||
3e47bd25 | 81 | struct eta_widget { |
3863d1ad | 82 | GtkWidget *names; |
c80b74b0 JA |
83 | struct multitext_widget iotype; |
84 | struct multitext_widget ioengine; | |
85 | struct multitext_widget iodepth; | |
3e47bd25 JA |
86 | GtkWidget *jobs; |
87 | GtkWidget *files; | |
88 | GtkWidget *read_bw; | |
89 | GtkWidget *read_iops; | |
90 | GtkWidget *cr_bw; | |
91 | GtkWidget *cr_iops; | |
92 | GtkWidget *write_bw; | |
93 | GtkWidget *write_iops; | |
94 | GtkWidget *cw_bw; | |
95 | GtkWidget *cw_iops; | |
96 | }; | |
97 | ||
2f99deb0 JA |
98 | struct gfio_graphs { |
99 | #define DRAWING_AREA_XDIM 1000 | |
100 | #define DRAWING_AREA_YDIM 400 | |
101 | GtkWidget *drawing_area; | |
2f99deb0 JA |
102 | struct graph *iops_graph; |
103 | struct graph *bandwidth_graph; | |
104 | }; | |
105 | ||
106 | /* | |
107 | * Main window widgets and data | |
108 | */ | |
ff1f3280 | 109 | struct gui { |
02421e69 | 110 | GtkUIManager *uimanager; |
38634cb1 JA |
111 | GtkRecentManager *recentmanager; |
112 | GtkActionGroup *actiongroup; | |
113 | guint recent_ui_id; | |
02421e69 | 114 | GtkWidget *menu; |
ff1f3280 | 115 | GtkWidget *window; |
2f99deb0 | 116 | GtkWidget *vbox; |
2f99deb0 JA |
117 | GtkWidget *thread_status_pb; |
118 | GtkWidget *buttonbox; | |
2f99deb0 JA |
119 | GtkWidget *notebook; |
120 | GtkWidget *error_info_bar; | |
121 | GtkWidget *error_label; | |
122 | GtkListStore *log_model; | |
123 | GtkWidget *log_tree; | |
124 | GtkWidget *log_view; | |
125 | struct gfio_graphs graphs; | |
126 | struct probe_widget probe; | |
127 | struct eta_widget eta; | |
128 | pthread_t server_t; | |
129 | ||
a9eccde4 JA |
130 | pthread_t t; |
131 | int handler_running; | |
132 | ||
2f99deb0 JA |
133 | struct flist_head list; |
134 | } main_ui; | |
135 | ||
85dd01e7 JA |
136 | enum { |
137 | GE_STATE_NEW = 1, | |
138 | GE_STATE_CONNECTED, | |
139 | GE_STATE_JOB_SENT, | |
140 | GE_STATE_JOB_STARTED, | |
141 | GE_STATE_JOB_RUNNING, | |
142 | GE_STATE_JOB_DONE, | |
143 | }; | |
144 | ||
2f99deb0 JA |
145 | /* |
146 | * Notebook entry | |
147 | */ | |
148 | struct gui_entry { | |
149 | struct flist_head list; | |
150 | struct gui *ui; | |
151 | ||
5b7573ab | 152 | GtkWidget *vbox; |
c80b74b0 | 153 | GtkWidget *job_notebook; |
04cc6b77 | 154 | GtkWidget *thread_status_pb; |
f3074008 SC |
155 | GtkWidget *buttonbox; |
156 | GtkWidget *button[ARRAYSIZE(buttonspeclist)]; | |
2f99deb0 | 157 | GtkWidget *notebook; |
0420ba6a JA |
158 | GtkWidget *error_info_bar; |
159 | GtkWidget *error_label; | |
f9d40b48 | 160 | GtkWidget *results_window; |
7da23b48 | 161 | GtkWidget *results_notebook; |
17b9721a | 162 | GtkUIManager *results_uimanager; |
17b9721a | 163 | GtkWidget *results_menu; |
7da23b48 | 164 | GtkWidget *disk_util_vbox; |
9b260bdf JA |
165 | GtkListStore *log_model; |
166 | GtkWidget *log_tree; | |
4cbe7211 | 167 | GtkWidget *log_view; |
2f99deb0 | 168 | struct gfio_graphs graphs; |
843ad237 | 169 | struct probe_widget probe; |
3e47bd25 | 170 | struct eta_widget eta; |
2f99deb0 JA |
171 | GtkWidget *page_label; |
172 | gint page_num; | |
85dd01e7 | 173 | unsigned int state; |
0420ba6a | 174 | |
09d574e3 JA |
175 | struct graph *clat_graph; |
176 | struct graph *lat_bucket_graph; | |
177 | ||
b9d2f30a | 178 | struct gfio_client *client; |
0420ba6a JA |
179 | int nr_job_files; |
180 | char **job_files; | |
2f99deb0 | 181 | }; |
ff1f3280 | 182 | |
781ccba6 JA |
183 | struct end_results { |
184 | struct group_run_stats gs; | |
185 | struct thread_stat ts; | |
186 | }; | |
187 | ||
e0681f3e | 188 | struct gfio_client { |
2f99deb0 | 189 | struct gui_entry *ge; |
b9d2f30a | 190 | struct fio_client *client; |
6b79c80c | 191 | GtkWidget *err_entry; |
dcaeb606 JA |
192 | unsigned int job_added; |
193 | struct thread_options o; | |
781ccba6 JA |
194 | |
195 | struct end_results *results; | |
196 | unsigned int nr_results; | |
7da23b48 JA |
197 | |
198 | struct cmd_du_pdu *du; | |
199 | unsigned int nr_du; | |
e0681f3e JA |
200 | }; |
201 | ||
9988ca70 JA |
202 | static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc); |
203 | static void gfio_update_thread_status_all(char *status_message, double perc); | |
c724926b | 204 | void report_error(GError *error); |
9988ca70 | 205 | |
2f99deb0 JA |
206 | static struct graph *setup_iops_graph(void) |
207 | { | |
208 | struct graph *g; | |
209 | ||
210 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
d8fbeefb | 211 | graph_title(g, "IOPS (IOs/sec)"); |
2f99deb0 | 212 | graph_x_title(g, "Time (secs)"); |
2f99deb0 JA |
213 | graph_add_label(g, "Read IOPS"); |
214 | graph_add_label(g, "Write IOPS"); | |
215 | graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13); | |
216 | graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0); | |
8577f4fd | 217 | line_graph_set_data_count_limit(g, gfio_graph_limit); |
d8fbeefb | 218 | graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); |
2f99deb0 JA |
219 | return g; |
220 | } | |
221 | ||
222 | static struct graph *setup_bandwidth_graph(void) | |
223 | { | |
224 | struct graph *g; | |
225 | ||
226 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
d8fbeefb | 227 | graph_title(g, "Bandwidth (bytes/sec)"); |
2f99deb0 | 228 | graph_x_title(g, "Time (secs)"); |
2f99deb0 JA |
229 | graph_add_label(g, "Read Bandwidth"); |
230 | graph_add_label(g, "Write Bandwidth"); | |
231 | graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13); | |
232 | graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0); | |
d8fbeefb | 233 | graph_set_base_offset(g, 1); |
2f99deb0 | 234 | line_graph_set_data_count_limit(g, 100); |
d8fbeefb | 235 | graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); |
2f99deb0 JA |
236 | return g; |
237 | } | |
238 | ||
239 | static void setup_graphs(struct gfio_graphs *g) | |
240 | { | |
241 | g->iops_graph = setup_iops_graph(); | |
242 | g->bandwidth_graph = setup_bandwidth_graph(); | |
243 | } | |
244 | ||
c80b74b0 JA |
245 | static void multitext_add_entry(struct multitext_widget *mt, const char *text) |
246 | { | |
247 | mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *)); | |
248 | mt->text[mt->max_text] = strdup(text); | |
249 | mt->max_text++; | |
250 | } | |
251 | ||
252 | static void multitext_set_entry(struct multitext_widget *mt, unsigned int index) | |
253 | { | |
254 | if (index >= mt->max_text) | |
255 | return; | |
da185431 | 256 | if (!mt->text || !mt->text[index]) |
c80b74b0 JA |
257 | return; |
258 | ||
259 | mt->cur_text = index; | |
260 | gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]); | |
261 | } | |
262 | ||
263 | static void multitext_update_entry(struct multitext_widget *mt, | |
264 | unsigned int index, const char *text) | |
265 | { | |
da185431 JA |
266 | if (!mt->text) |
267 | return; | |
268 | ||
c80b74b0 JA |
269 | if (mt->text[index]) |
270 | free(mt->text[index]); | |
271 | ||
272 | mt->text[index] = strdup(text); | |
273 | if (mt->cur_text == index) | |
274 | gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]); | |
275 | } | |
276 | ||
277 | static void multitext_free(struct multitext_widget *mt) | |
278 | { | |
279 | int i; | |
280 | ||
281 | gtk_entry_set_text(GTK_ENTRY(mt->entry), ""); | |
282 | ||
283 | for (i = 0; i < mt->max_text; i++) { | |
284 | if (mt->text[i]) | |
285 | free(mt->text[i]); | |
286 | } | |
287 | ||
288 | free(mt->text); | |
289 | mt->cur_text = -1; | |
290 | mt->max_text = 0; | |
291 | } | |
292 | ||
2f99deb0 JA |
293 | static void clear_ge_ui_info(struct gui_entry *ge) |
294 | { | |
295 | gtk_label_set_text(GTK_LABEL(ge->probe.hostname), ""); | |
296 | gtk_label_set_text(GTK_LABEL(ge->probe.os), ""); | |
297 | gtk_label_set_text(GTK_LABEL(ge->probe.arch), ""); | |
298 | gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), ""); | |
3863d1ad JA |
299 | #if 0 |
300 | /* should we empty it... */ | |
2f99deb0 | 301 | gtk_entry_set_text(GTK_ENTRY(ge->eta.name), ""); |
3863d1ad | 302 | #endif |
c80b74b0 JA |
303 | multitext_update_entry(&ge->eta.iotype, 0, ""); |
304 | multitext_update_entry(&ge->eta.ioengine, 0, ""); | |
305 | multitext_update_entry(&ge->eta.iodepth, 0, ""); | |
2f99deb0 JA |
306 | gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), ""); |
307 | gtk_entry_set_text(GTK_ENTRY(ge->eta.files), ""); | |
308 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), ""); | |
309 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), ""); | |
310 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), ""); | |
311 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), ""); | |
8663ea65 JA |
312 | } |
313 | ||
3863d1ad JA |
314 | static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label) |
315 | { | |
316 | GtkWidget *entry, *frame; | |
317 | ||
318 | frame = gtk_frame_new(label); | |
319 | entry = gtk_combo_box_new_text(); | |
320 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); | |
321 | gtk_container_add(GTK_CONTAINER(frame), entry); | |
322 | ||
323 | return entry; | |
324 | } | |
325 | ||
3650a3ca JA |
326 | static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label) |
327 | { | |
328 | GtkWidget *entry, *frame; | |
329 | ||
330 | frame = gtk_frame_new(label); | |
331 | entry = gtk_entry_new(); | |
1c1e4a5b | 332 | gtk_entry_set_editable(GTK_ENTRY(entry), 0); |
3650a3ca JA |
333 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); |
334 | gtk_container_add(GTK_CONTAINER(frame), entry); | |
335 | ||
336 | return entry; | |
337 | } | |
338 | ||
339 | static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) | |
340 | { | |
341 | GtkWidget *label_widget; | |
342 | GtkWidget *frame; | |
343 | ||
344 | frame = gtk_frame_new(label); | |
345 | label_widget = gtk_label_new(NULL); | |
346 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); | |
347 | gtk_container_add(GTK_CONTAINER(frame), label_widget); | |
348 | ||
349 | return label_widget; | |
350 | } | |
351 | ||
352 | static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval) | |
353 | { | |
354 | GtkWidget *button, *box; | |
355 | ||
356 | box = gtk_hbox_new(FALSE, 3); | |
357 | gtk_container_add(GTK_CONTAINER(hbox), box); | |
358 | ||
359 | button = gtk_spin_button_new_with_range(min, max, 1.0); | |
360 | gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); | |
361 | ||
362 | gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID); | |
363 | gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval); | |
364 | ||
365 | return button; | |
366 | } | |
367 | ||
3650a3ca JA |
368 | static void label_set_int_value(GtkWidget *entry, unsigned int val) |
369 | { | |
370 | char tmp[80]; | |
371 | ||
372 | sprintf(tmp, "%u", val); | |
373 | gtk_label_set_text(GTK_LABEL(entry), tmp); | |
374 | } | |
375 | ||
376 | static void entry_set_int_value(GtkWidget *entry, unsigned int val) | |
377 | { | |
378 | char tmp[80]; | |
379 | ||
380 | sprintf(tmp, "%u", val); | |
381 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
382 | } | |
383 | ||
16ce5adf JA |
384 | static void show_info_dialog(struct gui *ui, const char *title, |
385 | const char *message) | |
386 | { | |
387 | GtkWidget *dialog, *content, *label; | |
388 | ||
389 | dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window), | |
390 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, | |
391 | GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); | |
392 | ||
393 | content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); | |
394 | label = gtk_label_new(message); | |
395 | gtk_container_add(GTK_CONTAINER(content), label); | |
396 | gtk_widget_show_all(dialog); | |
397 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); | |
398 | gtk_dialog_run(GTK_DIALOG(dialog)); | |
399 | gtk_widget_destroy(dialog); | |
400 | } | |
401 | ||
781ccba6 JA |
402 | static void set_menu_entry_text(struct gui *ui, const char *path, |
403 | const char *text) | |
404 | { | |
405 | GtkWidget *w; | |
406 | ||
407 | w = gtk_ui_manager_get_widget(ui->uimanager, path); | |
408 | if (w) | |
409 | gtk_menu_item_set_label(GTK_MENU_ITEM(w), text); | |
410 | else | |
411 | fprintf(stderr, "gfio: can't find path %s\n", path); | |
412 | } | |
413 | ||
414 | ||
415 | static void set_menu_entry_visible(struct gui *ui, const char *path, int show) | |
416 | { | |
417 | GtkWidget *w; | |
418 | ||
419 | w = gtk_ui_manager_get_widget(ui->uimanager, path); | |
420 | if (w) | |
421 | gtk_widget_set_sensitive(w, show); | |
422 | else | |
423 | fprintf(stderr, "gfio: can't find path %s\n", path); | |
424 | } | |
425 | ||
426 | static void set_job_menu_visible(struct gui *ui, int visible) | |
427 | { | |
428 | set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible); | |
429 | } | |
430 | ||
431 | static void set_view_results_visible(struct gui *ui, int visible) | |
432 | { | |
433 | set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible); | |
434 | } | |
435 | ||
014f4024 JA |
436 | static const char *get_button_tooltip(struct button_spec *s, int sensitive) |
437 | { | |
438 | if (s->tooltiptext[sensitive]) | |
439 | return s->tooltiptext[sensitive]; | |
440 | ||
441 | return s->tooltiptext[0]; | |
442 | } | |
443 | ||
444 | static GtkWidget *add_button(GtkWidget *buttonbox, | |
445 | struct button_spec *buttonspec, gpointer data) | |
446 | { | |
447 | GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext); | |
448 | gboolean sens = buttonspec->start_sensitive; | |
449 | ||
450 | g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data); | |
451 | gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3); | |
452 | ||
453 | sens = buttonspec->start_sensitive; | |
454 | gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens)); | |
455 | gtk_widget_set_sensitive(button, sens); | |
456 | ||
457 | return button; | |
458 | } | |
459 | ||
460 | static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist, | |
461 | int nbuttons) | |
462 | { | |
463 | int i; | |
464 | ||
465 | for (i = 0; i < nbuttons; i++) | |
466 | ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge); | |
467 | } | |
468 | ||
85dd01e7 JA |
469 | /* |
470 | * Update sensitivity of job buttons and job menu items, based on the | |
471 | * state of the client. | |
472 | */ | |
473 | static void update_button_states(struct gui *ui, struct gui_entry *ge) | |
474 | { | |
475 | unsigned int connect_state, send_state, start_state, edit_state; | |
476 | const char *connect_str = NULL; | |
85dd01e7 JA |
477 | |
478 | switch (ge->state) { | |
479 | default: { | |
480 | char tmp[80]; | |
481 | ||
482 | sprintf(tmp, "Bad client state: %u\n", ge->state); | |
483 | show_info_dialog(ui, "Error", tmp); | |
484 | /* fall through to new state */ | |
485 | } | |
486 | ||
487 | case GE_STATE_NEW: | |
488 | connect_state = 1; | |
489 | edit_state = 0; | |
490 | connect_str = "Connect"; | |
491 | send_state = 0; | |
492 | start_state = 0; | |
493 | break; | |
494 | case GE_STATE_CONNECTED: | |
495 | connect_state = 1; | |
496 | edit_state = 0; | |
497 | connect_str = "Disconnect"; | |
498 | send_state = 1; | |
499 | start_state = 0; | |
500 | break; | |
501 | case GE_STATE_JOB_SENT: | |
502 | connect_state = 1; | |
503 | edit_state = 0; | |
504 | connect_str = "Disconnect"; | |
505 | send_state = 0; | |
506 | start_state = 1; | |
507 | break; | |
508 | case GE_STATE_JOB_STARTED: | |
509 | connect_state = 1; | |
510 | edit_state = 1; | |
511 | connect_str = "Disconnect"; | |
512 | send_state = 0; | |
513 | start_state = 1; | |
514 | break; | |
515 | case GE_STATE_JOB_RUNNING: | |
516 | connect_state = 1; | |
517 | edit_state = 0; | |
518 | connect_str = "Disconnect"; | |
519 | send_state = 0; | |
520 | start_state = 0; | |
521 | break; | |
522 | case GE_STATE_JOB_DONE: | |
523 | connect_state = 1; | |
524 | edit_state = 0; | |
525 | connect_str = "Connect"; | |
526 | send_state = 0; | |
527 | start_state = 0; | |
528 | break; | |
529 | } | |
530 | ||
531 | gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state); | |
532 | gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state); | |
533 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state); | |
534 | gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str); | |
014f4024 | 535 | gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state)); |
85dd01e7 | 536 | |
781ccba6 JA |
537 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state); |
538 | set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str); | |
85dd01e7 | 539 | |
781ccba6 JA |
540 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state); |
541 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state); | |
542 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state); | |
85dd01e7 | 543 | |
781ccba6 JA |
544 | if (ge->client && ge->client->nr_results) |
545 | set_view_results_visible(ui, 1); | |
546 | else | |
547 | set_view_results_visible(ui, 0); | |
85dd01e7 JA |
548 | } |
549 | ||
550 | static void gfio_set_state(struct gui_entry *ge, unsigned int state) | |
551 | { | |
552 | ge->state = state; | |
553 | update_button_states(ge->ui, ge); | |
554 | } | |
555 | ||
a269790c JA |
556 | #define ALIGN_LEFT 1 |
557 | #define ALIGN_RIGHT 2 | |
558 | #define INVISIBLE 4 | |
559 | #define UNSORTABLE 8 | |
560 | ||
561 | GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags) | |
562 | { | |
563 | GtkCellRenderer *renderer; | |
564 | GtkTreeViewColumn *col; | |
565 | double xalign = 0.0; /* left as default */ | |
566 | PangoAlignment align; | |
567 | gboolean visible; | |
568 | ||
569 | align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : | |
570 | (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : | |
571 | PANGO_ALIGN_CENTER; | |
572 | visible = !(flags & INVISIBLE); | |
573 | ||
574 | renderer = gtk_cell_renderer_text_new(); | |
575 | col = gtk_tree_view_column_new(); | |
576 | ||
577 | gtk_tree_view_column_set_title(col, title); | |
578 | if (!(flags & UNSORTABLE)) | |
579 | gtk_tree_view_column_set_sort_column_id(col, index); | |
580 | gtk_tree_view_column_set_resizable(col, TRUE); | |
581 | gtk_tree_view_column_pack_start(col, renderer, TRUE); | |
582 | gtk_tree_view_column_add_attribute(col, renderer, "text", index); | |
583 | gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL); | |
584 | switch (align) { | |
585 | case PANGO_ALIGN_LEFT: | |
586 | xalign = 0.0; | |
587 | break; | |
588 | case PANGO_ALIGN_CENTER: | |
589 | xalign = 0.5; | |
590 | break; | |
591 | case PANGO_ALIGN_RIGHT: | |
592 | xalign = 1.0; | |
593 | break; | |
594 | } | |
595 | gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); | |
596 | gtk_tree_view_column_set_visible(col, visible); | |
597 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); | |
598 | return col; | |
599 | } | |
600 | ||
9b260bdf JA |
601 | static void gfio_ui_setup_log(struct gui *ui) |
602 | { | |
603 | GtkTreeSelection *selection; | |
604 | GtkListStore *model; | |
605 | GtkWidget *tree_view; | |
606 | ||
607 | model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); | |
608 | ||
609 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
610 | gtk_widget_set_can_focus(tree_view, FALSE); | |
611 | ||
612 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); | |
613 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
661f741a JA |
614 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
615 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
9b260bdf JA |
616 | |
617 | tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE); | |
618 | tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE); | |
619 | tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE); | |
f095d563 | 620 | tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE); |
9b260bdf JA |
621 | |
622 | ui->log_model = model; | |
623 | ui->log_tree = tree_view; | |
624 | } | |
625 | ||
c57f254c SC |
626 | static struct graph *setup_clat_graph(char *title, unsigned int *ovals, |
627 | fio_fp64_t *plist, | |
628 | unsigned int len, | |
629 | double xdim, double ydim) | |
630 | { | |
631 | struct graph *g; | |
632 | int i; | |
633 | ||
634 | g = graph_new(xdim, ydim, gfio_graph_font); | |
635 | graph_title(g, title); | |
636 | graph_x_title(g, "Percentile"); | |
637 | ||
638 | for (i = 0; i < len; i++) { | |
639 | char fbuf[8]; | |
640 | ||
641 | sprintf(fbuf, "%2.2f%%", plist[i].u.f); | |
642 | graph_add_label(g, fbuf); | |
643 | graph_add_data(g, fbuf, (double) ovals[i]); | |
644 | } | |
645 | ||
646 | return g; | |
647 | } | |
648 | ||
a269790c JA |
649 | static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, |
650 | fio_fp64_t *plist, | |
651 | unsigned int len, | |
652 | const char *base, | |
653 | unsigned int scale) | |
654 | { | |
655 | GType types[FIO_IO_U_LIST_MAX_LEN]; | |
656 | GtkWidget *tree_view; | |
657 | GtkTreeSelection *selection; | |
658 | GtkListStore *model; | |
659 | GtkTreeIter iter; | |
660 | int i; | |
661 | ||
662 | for (i = 0; i < len; i++) | |
663 | types[i] = G_TYPE_INT; | |
664 | ||
665 | model = gtk_list_store_newv(len, types); | |
666 | ||
667 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
668 | gtk_widget_set_can_focus(tree_view, FALSE); | |
669 | ||
661f741a JA |
670 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
671 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
672 | ||
a269790c JA |
673 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
674 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
675 | ||
676 | for (i = 0; i < len; i++) { | |
677 | char fbuf[8]; | |
678 | ||
679 | sprintf(fbuf, "%2.2f%%", plist[i].u.f); | |
680 | tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE); | |
681 | } | |
682 | ||
683 | gtk_list_store_append(model, &iter); | |
684 | ||
e0681f3e JA |
685 | for (i = 0; i < len; i++) { |
686 | if (scale) | |
687 | ovals[i] = (ovals[i] + 999) / 1000; | |
a269790c | 688 | gtk_list_store_set(model, &iter, i, ovals[i], -1); |
e0681f3e | 689 | } |
a269790c JA |
690 | |
691 | return tree_view; | |
692 | } | |
693 | ||
09d574e3 | 694 | static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) |
c57f254c SC |
695 | { |
696 | struct graph *g = p; | |
697 | cairo_t *cr; | |
698 | ||
699 | cr = gdk_cairo_create(w->window); | |
700 | #if 0 | |
701 | if (graph_has_tooltips(g)) { | |
702 | g_object_set(w, "has-tooltip", TRUE, NULL); | |
703 | g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g); | |
704 | } | |
705 | #endif | |
706 | cairo_set_source_rgb(cr, 0, 0, 0); | |
707 | bar_graph_draw(g, cr); | |
708 | cairo_destroy(cr); | |
709 | ||
710 | return FALSE; | |
711 | } | |
712 | ||
09d574e3 JA |
713 | static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event, |
714 | gpointer data) | |
c57f254c SC |
715 | { |
716 | struct graph *g = data; | |
717 | ||
718 | graph_set_size(g, w->allocation.width, w->allocation.height); | |
719 | graph_set_size(g, w->allocation.width, w->allocation.height); | |
720 | graph_set_position(g, 0, 0); | |
721 | return TRUE; | |
722 | } | |
723 | ||
724 | static void gfio_show_clat_percentiles(struct gfio_client *gc, | |
725 | GtkWidget *vbox, struct thread_stat *ts, | |
a269790c JA |
726 | int ddir) |
727 | { | |
728 | unsigned int *io_u_plat = ts->io_u_plat[ddir]; | |
729 | unsigned long nr = ts->clat_stat[ddir].samples; | |
730 | fio_fp64_t *plist = ts->percentile_list; | |
731 | unsigned int *ovals, len, minv, maxv, scale_down; | |
732 | const char *base; | |
c57f254c | 733 | GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox; |
09d574e3 | 734 | struct gui_entry *ge = gc->ge; |
a269790c JA |
735 | char tmp[64]; |
736 | ||
737 | len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); | |
738 | if (!len) | |
739 | goto out; | |
740 | ||
741 | /* | |
742 | * We default to usecs, but if the value range is such that we | |
743 | * should scale down to msecs, do that. | |
744 | */ | |
745 | if (minv > 2000 && maxv > 99999) { | |
746 | scale_down = 1; | |
747 | base = "msec"; | |
748 | } else { | |
749 | scale_down = 0; | |
750 | base = "usec"; | |
751 | } | |
752 | ||
c57f254c | 753 | sprintf(tmp, "Completion percentiles (%s)", base); |
a269790c | 754 | tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); |
09d574e3 | 755 | ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0); |
a269790c | 756 | |
a269790c JA |
757 | frame = gtk_frame_new(tmp); |
758 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
759 | ||
c57f254c SC |
760 | completion_vbox = gtk_vbox_new(FALSE, 3); |
761 | gtk_container_add(GTK_CONTAINER(frame), completion_vbox); | |
a269790c | 762 | hbox = gtk_hbox_new(FALSE, 3); |
c57f254c SC |
763 | gtk_container_add(GTK_CONTAINER(completion_vbox), hbox); |
764 | drawing_area = gtk_drawing_area_new(); | |
765 | gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300); | |
766 | gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white); | |
767 | gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area); | |
09d574e3 JA |
768 | g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph); |
769 | g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph); | |
a269790c JA |
770 | |
771 | gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3); | |
772 | out: | |
773 | if (ovals) | |
774 | free(ovals); | |
775 | } | |
776 | ||
3650a3ca JA |
777 | static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, |
778 | unsigned long max, double mean, double dev) | |
779 | { | |
780 | const char *base = "(usec)"; | |
1c1e4a5b | 781 | GtkWidget *hbox, *label, *frame; |
3650a3ca JA |
782 | char *minp, *maxp; |
783 | char tmp[64]; | |
784 | ||
785 | if (!usec_to_msec(&min, &max, &mean, &dev)) | |
786 | base = "(msec)"; | |
787 | ||
788 | minp = num2str(min, 6, 1, 0); | |
789 | maxp = num2str(max, 6, 1, 0); | |
790 | ||
3650a3ca JA |
791 | sprintf(tmp, "%s %s", name, base); |
792 | frame = gtk_frame_new(tmp); | |
793 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
794 | ||
3650a3ca | 795 | hbox = gtk_hbox_new(FALSE, 3); |
1c1e4a5b | 796 | gtk_container_add(GTK_CONTAINER(frame), hbox); |
3650a3ca JA |
797 | |
798 | label = new_info_label_in_frame(hbox, "Minimum"); | |
799 | gtk_label_set_text(GTK_LABEL(label), minp); | |
800 | label = new_info_label_in_frame(hbox, "Maximum"); | |
801 | gtk_label_set_text(GTK_LABEL(label), maxp); | |
802 | label = new_info_label_in_frame(hbox, "Average"); | |
803 | sprintf(tmp, "%5.02f", mean); | |
804 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
805 | label = new_info_label_in_frame(hbox, "Standard deviation"); | |
806 | sprintf(tmp, "%5.02f", dev); | |
807 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
808 | ||
809 | free(minp); | |
810 | free(maxp); | |
811 | ||
812 | } | |
813 | ||
ca850992 JA |
814 | #define GFIO_CLAT 1 |
815 | #define GFIO_SLAT 2 | |
816 | #define GFIO_LAT 4 | |
817 | ||
c57f254c SC |
818 | static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox, |
819 | struct group_run_stats *rs, | |
3650a3ca JA |
820 | struct thread_stat *ts, int ddir) |
821 | { | |
822 | const char *ddir_label[2] = { "Read", "Write" }; | |
0b761306 | 823 | GtkWidget *frame, *label, *box, *vbox, *main_vbox; |
e0681f3e | 824 | unsigned long min[3], max[3], runt; |
3650a3ca | 825 | unsigned long long bw, iops; |
ca850992 | 826 | unsigned int flags = 0; |
e0681f3e | 827 | double mean[3], dev[3]; |
3650a3ca JA |
828 | char *io_p, *bw_p, *iops_p; |
829 | int i2p; | |
830 | ||
831 | if (!ts->runtime[ddir]) | |
832 | return; | |
833 | ||
834 | i2p = is_power_of_2(rs->kb_base); | |
835 | runt = ts->runtime[ddir]; | |
836 | ||
837 | bw = (1000 * ts->io_bytes[ddir]) / runt; | |
838 | io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); | |
839 | bw_p = num2str(bw, 6, 1, i2p); | |
840 | ||
841 | iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; | |
842 | iops_p = num2str(iops, 6, 1, 0); | |
843 | ||
844 | box = gtk_hbox_new(FALSE, 3); | |
845 | gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); | |
846 | ||
847 | frame = gtk_frame_new(ddir_label[ddir]); | |
848 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); | |
849 | ||
0b761306 JA |
850 | main_vbox = gtk_vbox_new(FALSE, 3); |
851 | gtk_container_add(GTK_CONTAINER(frame), main_vbox); | |
3650a3ca JA |
852 | |
853 | box = gtk_hbox_new(FALSE, 3); | |
0b761306 | 854 | gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3); |
3650a3ca JA |
855 | |
856 | label = new_info_label_in_frame(box, "IO"); | |
857 | gtk_label_set_text(GTK_LABEL(label), io_p); | |
858 | label = new_info_label_in_frame(box, "Bandwidth"); | |
859 | gtk_label_set_text(GTK_LABEL(label), bw_p); | |
860 | label = new_info_label_in_frame(box, "IOPS"); | |
861 | gtk_label_set_text(GTK_LABEL(label), iops_p); | |
862 | label = new_info_label_in_frame(box, "Runtime (msec)"); | |
863 | label_set_int_value(label, ts->runtime[ddir]); | |
864 | ||
e0681f3e | 865 | if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) { |
ca850992 JA |
866 | double p_of_agg = 100.0; |
867 | const char *bw_str = "KB"; | |
868 | char tmp[32]; | |
869 | ||
870 | if (rs->agg[ddir]) { | |
e0681f3e | 871 | p_of_agg = mean[0] * 100 / (double) rs->agg[ddir]; |
ca850992 JA |
872 | if (p_of_agg > 100.0) |
873 | p_of_agg = 100.0; | |
874 | } | |
875 | ||
e0681f3e JA |
876 | if (mean[0] > 999999.9) { |
877 | min[0] /= 1000.0; | |
878 | max[0] /= 1000.0; | |
879 | mean[0] /= 1000.0; | |
880 | dev[0] /= 1000.0; | |
ca850992 JA |
881 | bw_str = "MB"; |
882 | } | |
883 | ||
0b761306 JA |
884 | sprintf(tmp, "Bandwidth (%s)", bw_str); |
885 | frame = gtk_frame_new(tmp); | |
886 | gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); | |
ca850992 | 887 | |
0b761306 JA |
888 | box = gtk_hbox_new(FALSE, 3); |
889 | gtk_container_add(GTK_CONTAINER(frame), box); | |
ca850992 | 890 | |
0b761306 | 891 | label = new_info_label_in_frame(box, "Minimum"); |
e0681f3e | 892 | label_set_int_value(label, min[0]); |
0b761306 | 893 | label = new_info_label_in_frame(box, "Maximum"); |
e0681f3e | 894 | label_set_int_value(label, max[0]); |
0b761306 | 895 | label = new_info_label_in_frame(box, "Percentage of jobs"); |
ca850992 JA |
896 | sprintf(tmp, "%3.2f%%", p_of_agg); |
897 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
0b761306 | 898 | label = new_info_label_in_frame(box, "Average"); |
e0681f3e | 899 | sprintf(tmp, "%5.02f", mean[0]); |
ca850992 | 900 | gtk_label_set_text(GTK_LABEL(label), tmp); |
0b761306 | 901 | label = new_info_label_in_frame(box, "Standard deviation"); |
e0681f3e | 902 | sprintf(tmp, "%5.02f", dev[0]); |
ca850992 JA |
903 | gtk_label_set_text(GTK_LABEL(label), tmp); |
904 | } | |
905 | ||
e0681f3e | 906 | if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) |
2b089892 | 907 | flags |= GFIO_SLAT; |
e0681f3e | 908 | if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1])) |
2b089892 | 909 | flags |= GFIO_CLAT; |
e0681f3e | 910 | if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2])) |
2b089892 JA |
911 | flags |= GFIO_LAT; |
912 | ||
913 | if (flags) { | |
914 | frame = gtk_frame_new("Latency"); | |
915 | gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); | |
916 | ||
917 | vbox = gtk_vbox_new(FALSE, 3); | |
918 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
919 | ||
920 | if (flags & GFIO_SLAT) | |
e0681f3e | 921 | gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]); |
2b089892 | 922 | if (flags & GFIO_CLAT) |
e0681f3e | 923 | gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]); |
2b089892 | 924 | if (flags & GFIO_LAT) |
e0681f3e | 925 | gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]); |
2b089892 JA |
926 | } |
927 | ||
928 | if (ts->clat_percentiles) | |
c57f254c | 929 | gfio_show_clat_percentiles(gc, main_vbox, ts, ddir); |
2b089892 | 930 | |
3650a3ca JA |
931 | free(io_p); |
932 | free(bw_p); | |
933 | free(iops_p); | |
934 | } | |
935 | ||
09d574e3 JA |
936 | static struct graph *setup_lat_bucket_graph(const char *title, double *lat, |
937 | const char **labels, | |
938 | unsigned int len, | |
939 | double xdim, double ydim) | |
940 | { | |
941 | struct graph *g; | |
942 | int i; | |
943 | ||
944 | g = graph_new(xdim, ydim, gfio_graph_font); | |
945 | graph_title(g, title); | |
946 | graph_x_title(g, "Buckets"); | |
947 | ||
948 | for (i = 0; i < len; i++) { | |
949 | graph_add_label(g, labels[i]); | |
950 | graph_add_data(g, labels[i], lat[i]); | |
951 | } | |
952 | ||
953 | return g; | |
954 | } | |
955 | ||
956 | static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels, | |
957 | int num) | |
e5bd1347 JA |
958 | { |
959 | GtkWidget *tree_view; | |
960 | GtkTreeSelection *selection; | |
961 | GtkListStore *model; | |
962 | GtkTreeIter iter; | |
963 | GType *types; | |
09d574e3 | 964 | int i; |
e5bd1347 JA |
965 | |
966 | types = malloc(num * sizeof(GType)); | |
967 | ||
968 | for (i = 0; i < num; i++) | |
969 | types[i] = G_TYPE_STRING; | |
970 | ||
971 | model = gtk_list_store_newv(num, types); | |
972 | free(types); | |
973 | types = NULL; | |
974 | ||
975 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
976 | gtk_widget_set_can_focus(tree_view, FALSE); | |
977 | ||
661f741a JA |
978 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
979 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
980 | ||
e5bd1347 JA |
981 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
982 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
983 | ||
984 | for (i = 0; i < num; i++) | |
985 | tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); | |
986 | ||
987 | gtk_list_store_append(model, &iter); | |
988 | ||
989 | for (i = 0; i < num; i++) { | |
990 | char fbuf[32]; | |
991 | ||
992 | if (lat[i] <= 0.0) | |
993 | sprintf(fbuf, "0.00"); | |
994 | else | |
995 | sprintf(fbuf, "%3.2f%%", lat[i]); | |
996 | ||
997 | gtk_list_store_set(model, &iter, i, fbuf, -1); | |
998 | } | |
999 | ||
1000 | return tree_view; | |
1001 | } | |
1002 | ||
09d574e3 JA |
1003 | static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox, |
1004 | struct thread_stat *ts) | |
e5bd1347 | 1005 | { |
09d574e3 JA |
1006 | double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR]; |
1007 | const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u", | |
1008 | "250u", "500u", "750u", "1m", "2m", | |
1009 | "4m", "10m", "20m", "50m", "100m", | |
1010 | "250m", "500m", "750m", "1s", "2s", ">= 2s" }; | |
1011 | int start, end, i; | |
1012 | const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR; | |
1013 | GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area; | |
1014 | struct gui_entry *ge = gc->ge; | |
1015 | ||
1016 | stat_calc_lat_u(ts, io_u_lat); | |
1017 | stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]); | |
e5bd1347 | 1018 | |
09d574e3 JA |
1019 | /* |
1020 | * Found out which first bucket has entries, and which last bucket | |
1021 | */ | |
1022 | start = end = -1U; | |
1023 | for (i = 0; i < total; i++) { | |
1024 | if (io_u_lat[i] == 0.00) | |
1025 | continue; | |
1026 | ||
1027 | if (start == -1U) | |
1028 | start = i; | |
1029 | end = i; | |
e5bd1347 JA |
1030 | } |
1031 | ||
09d574e3 JA |
1032 | /* |
1033 | * No entries... | |
1034 | */ | |
1035 | if (start == -1U) | |
1036 | return; | |
1037 | ||
1038 | tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1); | |
1039 | ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0); | |
e5bd1347 | 1040 | |
09d574e3 JA |
1041 | frame = gtk_frame_new("Latency buckets"); |
1042 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
1043 | ||
1044 | completion_vbox = gtk_vbox_new(FALSE, 3); | |
1045 | gtk_container_add(GTK_CONTAINER(frame), completion_vbox); | |
1046 | hbox = gtk_hbox_new(FALSE, 3); | |
1047 | gtk_container_add(GTK_CONTAINER(completion_vbox), hbox); | |
1048 | ||
1049 | drawing_area = gtk_drawing_area_new(); | |
1050 | gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300); | |
1051 | gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white); | |
1052 | gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area); | |
1053 | g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph); | |
1054 | g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph); | |
1055 | ||
1056 | gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3); | |
e5bd1347 JA |
1057 | } |
1058 | ||
2e33101f JA |
1059 | static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts) |
1060 | { | |
1061 | GtkWidget *box, *frame, *entry; | |
1062 | double usr_cpu, sys_cpu; | |
1063 | unsigned long runtime; | |
1064 | char tmp[32]; | |
1065 | ||
1066 | runtime = ts->total_run_time; | |
1067 | if (runtime) { | |
1068 | double runt = (double) runtime; | |
1069 | ||
1070 | usr_cpu = (double) ts->usr_time * 100 / runt; | |
1071 | sys_cpu = (double) ts->sys_time * 100 / runt; | |
1072 | } else { | |
1073 | usr_cpu = 0; | |
1074 | sys_cpu = 0; | |
1075 | } | |
1076 | ||
1077 | frame = gtk_frame_new("OS resources"); | |
1078 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
1079 | ||
1080 | box = gtk_hbox_new(FALSE, 3); | |
1081 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1082 | ||
1083 | entry = new_info_entry_in_frame(box, "User CPU"); | |
1084 | sprintf(tmp, "%3.2f%%", usr_cpu); | |
1085 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
1086 | entry = new_info_entry_in_frame(box, "System CPU"); | |
1087 | sprintf(tmp, "%3.2f%%", sys_cpu); | |
1088 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
1089 | entry = new_info_entry_in_frame(box, "Context switches"); | |
1090 | entry_set_int_value(entry, ts->ctx); | |
1091 | entry = new_info_entry_in_frame(box, "Major faults"); | |
1092 | entry_set_int_value(entry, ts->majf); | |
1093 | entry = new_info_entry_in_frame(box, "Minor faults"); | |
1094 | entry_set_int_value(entry, ts->minf); | |
1095 | } | |
19998dbc JA |
1096 | static void gfio_add_sc_depths_tree(GtkListStore *model, |
1097 | struct thread_stat *ts, unsigned int len, | |
1098 | int submit) | |
2e33101f JA |
1099 | { |
1100 | double io_u_dist[FIO_IO_U_MAP_NR]; | |
2e33101f | 1101 | GtkTreeIter iter; |
19998dbc JA |
1102 | /* Bits 0, and 3-8 */ |
1103 | const int add_mask = 0x1f9; | |
1104 | int i, j; | |
2e33101f | 1105 | |
19998dbc JA |
1106 | if (submit) |
1107 | stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist); | |
1108 | else | |
1109 | stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist); | |
2e33101f | 1110 | |
19998dbc | 1111 | gtk_list_store_append(model, &iter); |
2e33101f | 1112 | |
19998dbc | 1113 | gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1); |
2e33101f | 1114 | |
19998dbc JA |
1115 | for (i = 1, j = 0; i < len; i++) { |
1116 | char fbuf[32]; | |
2e33101f | 1117 | |
19998dbc JA |
1118 | if (!(add_mask & (1UL << (i - 1)))) |
1119 | sprintf(fbuf, "0.0%%"); | |
1120 | else { | |
1121 | sprintf(fbuf, "%3.1f%%", io_u_dist[j]); | |
1122 | j++; | |
1123 | } | |
2e33101f | 1124 | |
19998dbc JA |
1125 | gtk_list_store_set(model, &iter, i, fbuf, -1); |
1126 | } | |
2e33101f | 1127 | |
19998dbc | 1128 | } |
2e33101f | 1129 | |
19998dbc JA |
1130 | static void gfio_add_total_depths_tree(GtkListStore *model, |
1131 | struct thread_stat *ts, unsigned int len) | |
1132 | { | |
1133 | double io_u_dist[FIO_IO_U_MAP_NR]; | |
1134 | GtkTreeIter iter; | |
1135 | /* Bits 1-6, and 8 */ | |
1136 | const int add_mask = 0x17e; | |
1137 | int i, j; | |
1138 | ||
1139 | stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); | |
2e33101f JA |
1140 | |
1141 | gtk_list_store_append(model, &iter); | |
1142 | ||
19998dbc JA |
1143 | gtk_list_store_set(model, &iter, 0, "Total", -1); |
1144 | ||
1145 | for (i = 1, j = 0; i < len; i++) { | |
2e33101f JA |
1146 | char fbuf[32]; |
1147 | ||
19998dbc JA |
1148 | if (!(add_mask & (1UL << (i - 1)))) |
1149 | sprintf(fbuf, "0.0%%"); | |
1150 | else { | |
1151 | sprintf(fbuf, "%3.1f%%", io_u_dist[j]); | |
1152 | j++; | |
2e33101f JA |
1153 | } |
1154 | ||
2e33101f JA |
1155 | gtk_list_store_set(model, &iter, i, fbuf, -1); |
1156 | } | |
1157 | ||
19998dbc | 1158 | } |
2e33101f | 1159 | |
19998dbc JA |
1160 | static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) |
1161 | { | |
1162 | GtkWidget *frame, *box, *tree_view; | |
1163 | GtkTreeSelection *selection; | |
1164 | GtkListStore *model; | |
1165 | GType types[FIO_IO_U_MAP_NR + 1]; | |
1166 | int i; | |
1167 | #define NR_LABELS 10 | |
1168 | const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" }; | |
2e33101f | 1169 | |
19998dbc JA |
1170 | frame = gtk_frame_new("IO depths"); |
1171 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
2e33101f | 1172 | |
19998dbc JA |
1173 | box = gtk_hbox_new(FALSE, 3); |
1174 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2e33101f | 1175 | |
19998dbc JA |
1176 | for (i = 0; i < NR_LABELS; i++) |
1177 | types[i] = G_TYPE_STRING; | |
2e33101f | 1178 | |
19998dbc | 1179 | model = gtk_list_store_newv(NR_LABELS, types); |
2e33101f | 1180 | |
19998dbc JA |
1181 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); |
1182 | gtk_widget_set_can_focus(tree_view, FALSE); | |
2e33101f | 1183 | |
661f741a JA |
1184 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
1185 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
1186 | ||
19998dbc JA |
1187 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
1188 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
1189 | ||
1190 | for (i = 0; i < NR_LABELS; i++) | |
1191 | tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); | |
1192 | ||
1193 | gfio_add_total_depths_tree(model, ts, NR_LABELS); | |
1194 | gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1); | |
1195 | gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0); | |
2e33101f JA |
1196 | |
1197 | gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); | |
1198 | } | |
1199 | ||
f9d40b48 JA |
1200 | static gboolean results_window_delete(GtkWidget *w, gpointer data) |
1201 | { | |
2f99deb0 | 1202 | struct gui_entry *ge = (struct gui_entry *) data; |
f9d40b48 JA |
1203 | |
1204 | gtk_widget_destroy(w); | |
2f99deb0 JA |
1205 | ge->results_window = NULL; |
1206 | ge->results_notebook = NULL; | |
f9d40b48 JA |
1207 | return TRUE; |
1208 | } | |
1209 | ||
17b9721a JA |
1210 | static void results_close(GtkWidget *w, gpointer *data) |
1211 | { | |
1212 | struct gui_entry *ge = (struct gui_entry *) data; | |
1213 | ||
1214 | gtk_widget_destroy(ge->results_window); | |
1215 | } | |
1216 | ||
1217 | static GtkActionEntry results_menu_items[] = { | |
1218 | { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, | |
1219 | { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL}, | |
1220 | { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) }, | |
1221 | }; | |
1222 | static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]); | |
1223 | ||
1224 | static const gchar *results_ui_string = " \ | |
1225 | <ui> \ | |
1226 | <menubar name=\"MainMenu\"> \ | |
1227 | <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ | |
1228 | <menuitem name=\"Close\" action=\"CloseFile\" /> \ | |
1229 | </menu> \ | |
1230 | <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \ | |
1231 | </menu>\ | |
1232 | </menubar> \ | |
1233 | </ui> \ | |
1234 | "; | |
1235 | ||
1236 | static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge) | |
1237 | { | |
1238 | GtkActionGroup *action_group; | |
1239 | GtkWidget *widget; | |
1240 | GError *error = 0; | |
1241 | ||
1242 | ge->results_uimanager = gtk_ui_manager_new(); | |
1243 | ||
1244 | action_group = gtk_action_group_new("ResultsMenu"); | |
1245 | gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge); | |
1246 | ||
1247 | gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0); | |
1248 | gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error); | |
1249 | ||
1250 | gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager)); | |
1251 | ||
1252 | widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu"); | |
1253 | return widget; | |
1254 | } | |
1255 | ||
2f99deb0 | 1256 | static GtkWidget *get_results_window(struct gui_entry *ge) |
f9d40b48 | 1257 | { |
17b9721a | 1258 | GtkWidget *win, *notebook, *vbox; |
f9d40b48 | 1259 | |
2f99deb0 JA |
1260 | if (ge->results_window) |
1261 | return ge->results_notebook; | |
f9d40b48 JA |
1262 | |
1263 | win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
1264 | gtk_window_set_title(GTK_WINDOW(win), "Results"); | |
b01329d0 | 1265 | gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768); |
2f99deb0 JA |
1266 | g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge); |
1267 | g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge); | |
f9d40b48 | 1268 | |
17b9721a JA |
1269 | vbox = gtk_vbox_new(FALSE, 0); |
1270 | gtk_container_add(GTK_CONTAINER(win), vbox); | |
1271 | ||
1272 | ge->results_menu = get_results_menubar(win, ge); | |
1273 | gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0); | |
1274 | ||
f9d40b48 | 1275 | notebook = gtk_notebook_new(); |
0aa928c4 JA |
1276 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1); |
1277 | gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); | |
17b9721a | 1278 | gtk_container_add(GTK_CONTAINER(vbox), notebook); |
f9d40b48 | 1279 | |
2f99deb0 JA |
1280 | ge->results_window = win; |
1281 | ge->results_notebook = notebook; | |
1282 | return ge->results_notebook; | |
f9d40b48 JA |
1283 | } |
1284 | ||
7da23b48 JA |
1285 | static void disk_util_destroy(GtkWidget *w, gpointer data) |
1286 | { | |
1287 | struct gui_entry *ge = (struct gui_entry *) data; | |
1288 | ||
1289 | ge->disk_util_vbox = NULL; | |
1290 | gtk_widget_destroy(w); | |
1291 | } | |
1292 | ||
1293 | static int __gfio_disk_util_show(GtkWidget *res_notebook, | |
1294 | struct gfio_client *gc, struct cmd_du_pdu *p) | |
1295 | { | |
1296 | GtkWidget *box, *frame, *entry, *vbox; | |
1297 | struct gui_entry *ge = gc->ge; | |
1298 | double util; | |
1299 | char tmp[16]; | |
1300 | ||
1301 | res_notebook = get_results_window(ge); | |
1302 | ||
1303 | if (!ge->disk_util_vbox) { | |
1304 | vbox = gtk_vbox_new(FALSE, 3); | |
1305 | gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization")); | |
1306 | ge->disk_util_vbox = vbox; | |
1307 | g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge); | |
1308 | } | |
1309 | ||
1310 | vbox = gtk_vbox_new(FALSE, 3); | |
1311 | gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox); | |
1312 | ||
1313 | frame = gtk_frame_new((char *) p->dus.name); | |
1314 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); | |
1315 | ||
1316 | box = gtk_vbox_new(FALSE, 3); | |
1317 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1318 | ||
1319 | frame = gtk_frame_new("Read"); | |
1320 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1321 | vbox = gtk_hbox_new(TRUE, 3); | |
1322 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1323 | entry = new_info_entry_in_frame(vbox, "IOs"); | |
1324 | entry_set_int_value(entry, p->dus.ios[0]); | |
1325 | entry = new_info_entry_in_frame(vbox, "Merges"); | |
1326 | entry_set_int_value(entry, p->dus.merges[0]); | |
1327 | entry = new_info_entry_in_frame(vbox, "Sectors"); | |
1328 | entry_set_int_value(entry, p->dus.sectors[0]); | |
1329 | entry = new_info_entry_in_frame(vbox, "Ticks"); | |
1330 | entry_set_int_value(entry, p->dus.ticks[0]); | |
1331 | ||
1332 | frame = gtk_frame_new("Write"); | |
1333 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1334 | vbox = gtk_hbox_new(TRUE, 3); | |
1335 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1336 | entry = new_info_entry_in_frame(vbox, "IOs"); | |
1337 | entry_set_int_value(entry, p->dus.ios[1]); | |
1338 | entry = new_info_entry_in_frame(vbox, "Merges"); | |
1339 | entry_set_int_value(entry, p->dus.merges[1]); | |
1340 | entry = new_info_entry_in_frame(vbox, "Sectors"); | |
1341 | entry_set_int_value(entry, p->dus.sectors[1]); | |
1342 | entry = new_info_entry_in_frame(vbox, "Ticks"); | |
1343 | entry_set_int_value(entry, p->dus.ticks[1]); | |
1344 | ||
1345 | frame = gtk_frame_new("Shared"); | |
1346 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1347 | vbox = gtk_hbox_new(TRUE, 3); | |
1348 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1349 | entry = new_info_entry_in_frame(vbox, "IO ticks"); | |
1350 | entry_set_int_value(entry, p->dus.io_ticks); | |
1351 | entry = new_info_entry_in_frame(vbox, "Time in queue"); | |
1352 | entry_set_int_value(entry, p->dus.time_in_queue); | |
1353 | ||
1354 | util = 0.0; | |
1355 | if (p->dus.msec) | |
1356 | util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec; | |
1357 | if (util > 100.0) | |
1358 | util = 100.0; | |
1359 | ||
1360 | sprintf(tmp, "%3.2f%%", util); | |
1361 | entry = new_info_entry_in_frame(vbox, "Disk utilization"); | |
1362 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
1363 | ||
1364 | gtk_widget_show_all(ge->results_window); | |
1365 | return 0; | |
1366 | } | |
1367 | ||
1368 | static int gfio_disk_util_show(struct gfio_client *gc) | |
1369 | { | |
1370 | struct gui_entry *ge = gc->ge; | |
1371 | GtkWidget *res_notebook; | |
1372 | int i; | |
1373 | ||
1374 | if (!gc->nr_du) | |
1375 | return 1; | |
1376 | ||
1377 | res_notebook = get_results_window(ge); | |
1378 | ||
1379 | for (i = 0; i < gc->nr_du; i++) { | |
1380 | struct cmd_du_pdu *p = &gc->du[i]; | |
1381 | ||
1382 | __gfio_disk_util_show(res_notebook, gc, p); | |
1383 | } | |
1384 | ||
1385 | gtk_widget_show_all(ge->results_window); | |
1386 | return 0; | |
1387 | } | |
1388 | ||
781ccba6 JA |
1389 | static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts, |
1390 | struct group_run_stats *rs) | |
3650a3ca | 1391 | { |
781ccba6 | 1392 | unsigned int nr = gc->nr_results; |
3650a3ca | 1393 | |
781ccba6 JA |
1394 | gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results)); |
1395 | memcpy(&gc->results[nr].ts, ts, sizeof(*ts)); | |
1396 | memcpy(&gc->results[nr].gs, rs, sizeof(*rs)); | |
1397 | gc->nr_results++; | |
1398 | } | |
3650a3ca | 1399 | |
781ccba6 JA |
1400 | static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc, |
1401 | struct thread_stat *ts, | |
1402 | struct group_run_stats *rs) | |
1403 | { | |
1404 | GtkWidget *box, *vbox, *entry, *scroll; | |
3650a3ca | 1405 | |
b01329d0 JA |
1406 | scroll = gtk_scrolled_window_new(NULL, NULL); |
1407 | gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); | |
1408 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1409 | ||
3650a3ca | 1410 | vbox = gtk_vbox_new(FALSE, 3); |
3650a3ca | 1411 | |
b01329d0 JA |
1412 | box = gtk_hbox_new(FALSE, 0); |
1413 | gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5); | |
1414 | ||
1415 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); | |
3650a3ca | 1416 | |
781ccba6 | 1417 | gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name)); |
f9d40b48 | 1418 | |
3650a3ca JA |
1419 | entry = new_info_entry_in_frame(box, "Name"); |
1420 | gtk_entry_set_text(GTK_ENTRY(entry), ts->name); | |
1421 | if (strlen(ts->description)) { | |
1422 | entry = new_info_entry_in_frame(box, "Description"); | |
1423 | gtk_entry_set_text(GTK_ENTRY(entry), ts->description); | |
1424 | } | |
1425 | entry = new_info_entry_in_frame(box, "Group ID"); | |
1426 | entry_set_int_value(entry, ts->groupid); | |
1427 | entry = new_info_entry_in_frame(box, "Jobs"); | |
1428 | entry_set_int_value(entry, ts->members); | |
6b79c80c | 1429 | gc->err_entry = entry = new_info_entry_in_frame(box, "Error"); |
3650a3ca JA |
1430 | entry_set_int_value(entry, ts->error); |
1431 | entry = new_info_entry_in_frame(box, "PID"); | |
1432 | entry_set_int_value(entry, ts->pid); | |
1433 | ||
1434 | if (ts->io_bytes[DDIR_READ]) | |
c57f254c | 1435 | gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ); |
3650a3ca | 1436 | if (ts->io_bytes[DDIR_WRITE]) |
c57f254c | 1437 | gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE); |
3650a3ca | 1438 | |
09d574e3 | 1439 | gfio_show_latency_buckets(gc, vbox, ts); |
2e33101f JA |
1440 | gfio_show_cpu_usage(vbox, ts); |
1441 | gfio_show_io_depths(vbox, ts); | |
781ccba6 JA |
1442 | } |
1443 | ||
1444 | static void gfio_display_end_results(struct gfio_client *gc) | |
1445 | { | |
60d0929e JA |
1446 | struct gui_entry *ge = gc->ge; |
1447 | GtkWidget *res_notebook; | |
781ccba6 JA |
1448 | int i; |
1449 | ||
60d0929e | 1450 | res_notebook = get_results_window(ge); |
781ccba6 JA |
1451 | |
1452 | for (i = 0; i < gc->nr_results; i++) { | |
1453 | struct end_results *e = &gc->results[i]; | |
1454 | ||
60d0929e | 1455 | __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs); |
781ccba6 | 1456 | } |
e5bd1347 | 1457 | |
7da23b48 JA |
1458 | if (gfio_disk_util_show(gc)) |
1459 | gtk_widget_show_all(ge->results_window); | |
781ccba6 JA |
1460 | } |
1461 | ||
1462 | static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, | |
1463 | struct group_run_stats *rs) | |
1464 | { | |
1465 | struct gfio_client *gc = client->client_data; | |
1466 | ||
1467 | gfio_add_end_results(gc, ts, rs); | |
1468 | ||
1469 | gdk_threads_enter(); | |
1470 | gfio_display_end_results(gc); | |
3650a3ca JA |
1471 | gdk_threads_leave(); |
1472 | } | |
1473 | ||
084d1c6f | 1474 | static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) |
a1820207 | 1475 | { |
9b260bdf | 1476 | struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload; |
2f99deb0 | 1477 | struct gui *ui = &main_ui; |
9b260bdf JA |
1478 | GtkTreeIter iter; |
1479 | struct tm *tm; | |
1480 | time_t sec; | |
1481 | char tmp[64], timebuf[80]; | |
1482 | ||
1483 | sec = p->log_sec; | |
1484 | tm = localtime(&sec); | |
1485 | strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm); | |
1486 | sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000); | |
736f2dff | 1487 | |
736f2dff | 1488 | gdk_threads_enter(); |
9b260bdf | 1489 | |
2f99deb0 JA |
1490 | gtk_list_store_append(ui->log_model, &iter); |
1491 | gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1); | |
1492 | gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1); | |
1493 | gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1); | |
1494 | gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1); | |
9b260bdf | 1495 | |
6b79c80c | 1496 | if (p->level == FIO_LOG_ERR) |
2f99deb0 | 1497 | view_log(NULL, (gpointer) ui); |
6b79c80c | 1498 | |
736f2dff | 1499 | gdk_threads_leave(); |
a1820207 SC |
1500 | } |
1501 | ||
1502 | static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) | |
1503 | { | |
e0681f3e JA |
1504 | struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload; |
1505 | struct gfio_client *gc = client->client_data; | |
7da23b48 | 1506 | unsigned int nr = gc->nr_du; |
e0681f3e | 1507 | |
7da23b48 JA |
1508 | gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu)); |
1509 | memcpy(&gc->du[nr], p, sizeof(*p)); | |
1510 | gc->nr_du++; | |
e0681f3e | 1511 | |
7da23b48 JA |
1512 | gdk_threads_enter(); |
1513 | gfio_disk_util_show(gc); | |
0050e5f2 | 1514 | gdk_threads_leave(); |
a1820207 SC |
1515 | } |
1516 | ||
3650a3ca JA |
1517 | extern int sum_stat_clients; |
1518 | extern struct thread_stat client_ts; | |
1519 | extern struct group_run_stats client_gs; | |
1520 | ||
1521 | static int sum_stat_nr; | |
1522 | ||
89e5fad9 JA |
1523 | static void gfio_thread_status_op(struct fio_client *client, |
1524 | struct fio_net_cmd *cmd) | |
a1820207 | 1525 | { |
3650a3ca JA |
1526 | struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; |
1527 | ||
1528 | gfio_display_ts(client, &p->ts, &p->rs); | |
1529 | ||
1530 | if (sum_stat_clients == 1) | |
1531 | return; | |
1532 | ||
1533 | sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); | |
1534 | sum_group_stats(&client_gs, &p->rs); | |
1535 | ||
1536 | client_ts.members++; | |
2f122b13 | 1537 | client_ts.thread_number = p->ts.thread_number; |
3650a3ca JA |
1538 | client_ts.groupid = p->ts.groupid; |
1539 | ||
1540 | if (++sum_stat_nr == sum_stat_clients) { | |
1541 | strcpy(client_ts.name, "All clients"); | |
1542 | gfio_display_ts(client, &client_ts, &client_gs); | |
1543 | } | |
a1820207 SC |
1544 | } |
1545 | ||
89e5fad9 JA |
1546 | static void gfio_group_stats_op(struct fio_client *client, |
1547 | struct fio_net_cmd *cmd) | |
a1820207 | 1548 | { |
98ceabd6 | 1549 | /* We're ignoring group stats for now */ |
a1820207 SC |
1550 | } |
1551 | ||
2f99deb0 JA |
1552 | static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event, |
1553 | gpointer data) | |
3ea48b88 | 1554 | { |
2f99deb0 JA |
1555 | struct gfio_graphs *g = data; |
1556 | ||
57f9d28e SC |
1557 | graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height); |
1558 | graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0); | |
1559 | graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height); | |
1560 | graph_set_position(g->bandwidth_graph, 0, 0); | |
3ea48b88 SC |
1561 | return TRUE; |
1562 | } | |
1563 | ||
57f9d28e SC |
1564 | static void draw_graph(struct graph *g, cairo_t *cr) |
1565 | { | |
1566 | line_graph_draw(g, cr); | |
1567 | cairo_stroke(cr); | |
1568 | } | |
1569 | ||
93e2db2b JA |
1570 | static gboolean graph_tooltip(GtkWidget *w, gint x, gint y, |
1571 | gboolean keyboard_mode, GtkTooltip *tooltip, | |
1572 | gpointer data) | |
1573 | { | |
1574 | struct gfio_graphs *g = data; | |
1575 | const char *text = NULL; | |
1576 | ||
1577 | if (graph_contains_xy(g->iops_graph, x, y)) | |
1578 | text = graph_find_tooltip(g->iops_graph, x, y); | |
1579 | else if (graph_contains_xy(g->bandwidth_graph, x, y)) | |
1580 | text = graph_find_tooltip(g->bandwidth_graph, x, y); | |
1581 | ||
1582 | if (text) { | |
1583 | gtk_tooltip_set_text(tooltip, text); | |
1584 | return TRUE; | |
1585 | } | |
1586 | ||
1587 | return FALSE; | |
1588 | } | |
1589 | ||
2fd3bb0e JA |
1590 | static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) |
1591 | { | |
2f99deb0 | 1592 | struct gfio_graphs *g = p; |
2fd3bb0e JA |
1593 | cairo_t *cr; |
1594 | ||
1595 | cr = gdk_cairo_create(w->window); | |
93e2db2b JA |
1596 | |
1597 | if (graph_has_tooltips(g->iops_graph) || | |
1598 | graph_has_tooltips(g->bandwidth_graph)) { | |
1599 | g_object_set(w, "has-tooltip", TRUE, NULL); | |
1600 | g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g); | |
1601 | } | |
1602 | ||
2fd3bb0e | 1603 | cairo_set_source_rgb(cr, 0, 0, 0); |
57f9d28e SC |
1604 | draw_graph(g->iops_graph, cr); |
1605 | draw_graph(g->bandwidth_graph, cr); | |
2fd3bb0e JA |
1606 | cairo_destroy(cr); |
1607 | ||
1608 | return FALSE; | |
1609 | } | |
1610 | ||
2f99deb0 JA |
1611 | /* |
1612 | * Client specific ETA | |
1613 | */ | |
1614 | static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je) | |
3e47bd25 | 1615 | { |
2f99deb0 JA |
1616 | struct gfio_client *gc = client->client_data; |
1617 | struct gui_entry *ge = gc->ge; | |
3e47bd25 JA |
1618 | static int eta_good; |
1619 | char eta_str[128]; | |
1620 | char output[256]; | |
1621 | char tmp[32]; | |
1622 | double perc = 0.0; | |
1623 | int i2p = 0; | |
1624 | ||
0050e5f2 JA |
1625 | gdk_threads_enter(); |
1626 | ||
3e47bd25 JA |
1627 | eta_str[0] = '\0'; |
1628 | output[0] = '\0'; | |
1629 | ||
1630 | if (je->eta_sec != INT_MAX && je->elapsed_sec) { | |
1631 | perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); | |
1632 | eta_to_str(eta_str, je->eta_sec); | |
1633 | } | |
1634 | ||
1635 | sprintf(tmp, "%u", je->nr_running); | |
2f99deb0 | 1636 | gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp); |
3e47bd25 | 1637 | sprintf(tmp, "%u", je->files_open); |
2f99deb0 | 1638 | gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp); |
3e47bd25 JA |
1639 | |
1640 | #if 0 | |
1641 | if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { | |
1642 | if (je->m_rate || je->t_rate) { | |
1643 | char *tr, *mr; | |
1644 | ||
1645 | mr = num2str(je->m_rate, 4, 0, i2p); | |
1646 | tr = num2str(je->t_rate, 4, 0, i2p); | |
2f99deb0 | 1647 | gtk_entry_set_text(GTK_ENTRY(ge->eta); |
3e47bd25 JA |
1648 | p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); |
1649 | free(tr); | |
1650 | free(mr); | |
1651 | } else if (je->m_iops || je->t_iops) | |
1652 | p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); | |
ebbd89cc | 1653 | |
2f99deb0 JA |
1654 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---"); |
1655 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---"); | |
1656 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---"); | |
1657 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---"); | |
3e47bd25 JA |
1658 | #endif |
1659 | ||
1660 | if (je->eta_sec != INT_MAX && je->nr_running) { | |
1661 | char *iops_str[2]; | |
1662 | char *rate_str[2]; | |
1663 | ||
1664 | if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) | |
1665 | strcpy(output, "-.-% done"); | |
1666 | else { | |
1667 | eta_good = 1; | |
1668 | perc *= 100.0; | |
1669 | sprintf(output, "%3.1f%% done", perc); | |
1670 | } | |
1671 | ||
1672 | rate_str[0] = num2str(je->rate[0], 5, 10, i2p); | |
1673 | rate_str[1] = num2str(je->rate[1], 5, 10, i2p); | |
1674 | ||
1675 | iops_str[0] = num2str(je->iops[0], 4, 1, 0); | |
1676 | iops_str[1] = num2str(je->iops[1], 4, 1, 0); | |
1677 | ||
2f99deb0 JA |
1678 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]); |
1679 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]); | |
1680 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]); | |
1681 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]); | |
3e47bd25 | 1682 | |
93e2db2b JA |
1683 | graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]); |
1684 | graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]); | |
1685 | graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]); | |
1686 | graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]); | |
2f99deb0 JA |
1687 | |
1688 | free(rate_str[0]); | |
1689 | free(rate_str[1]); | |
1690 | free(iops_str[0]); | |
1691 | free(iops_str[1]); | |
1692 | } | |
1693 | ||
1694 | if (eta_str[0]) { | |
1695 | char *dst = output + strlen(output); | |
1696 | ||
1697 | sprintf(dst, " - %s", eta_str); | |
1698 | } | |
1699 | ||
9988ca70 | 1700 | gfio_update_thread_status(ge, output, perc); |
2f99deb0 JA |
1701 | gdk_threads_leave(); |
1702 | } | |
1703 | ||
1704 | /* | |
1705 | * Update ETA in main window for all clients | |
1706 | */ | |
1707 | static void gfio_update_all_eta(struct jobs_eta *je) | |
1708 | { | |
1709 | struct gui *ui = &main_ui; | |
1710 | static int eta_good; | |
1711 | char eta_str[128]; | |
1712 | char output[256]; | |
1713 | double perc = 0.0; | |
1714 | int i2p = 0; | |
1715 | ||
1716 | gdk_threads_enter(); | |
1717 | ||
1718 | eta_str[0] = '\0'; | |
1719 | output[0] = '\0'; | |
1720 | ||
1721 | if (je->eta_sec != INT_MAX && je->elapsed_sec) { | |
1722 | perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); | |
1723 | eta_to_str(eta_str, je->eta_sec); | |
1724 | } | |
1725 | ||
1726 | #if 0 | |
1727 | if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { | |
1728 | if (je->m_rate || je->t_rate) { | |
1729 | char *tr, *mr; | |
1730 | ||
1731 | mr = num2str(je->m_rate, 4, 0, i2p); | |
1732 | tr = num2str(je->t_rate, 4, 0, i2p); | |
1733 | gtk_entry_set_text(GTK_ENTRY(ui->eta); | |
1734 | p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); | |
1735 | free(tr); | |
1736 | free(mr); | |
1737 | } else if (je->m_iops || je->t_iops) | |
1738 | p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); | |
1739 | ||
1740 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---"); | |
1741 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---"); | |
1742 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---"); | |
1743 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---"); | |
1744 | #endif | |
1745 | ||
3863d1ad JA |
1746 | entry_set_int_value(ui->eta.jobs, je->nr_running); |
1747 | ||
2f99deb0 JA |
1748 | if (je->eta_sec != INT_MAX && je->nr_running) { |
1749 | char *iops_str[2]; | |
1750 | char *rate_str[2]; | |
1751 | ||
1752 | if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) | |
1753 | strcpy(output, "-.-% done"); | |
1754 | else { | |
1755 | eta_good = 1; | |
1756 | perc *= 100.0; | |
1757 | sprintf(output, "%3.1f%% done", perc); | |
1758 | } | |
1759 | ||
1760 | rate_str[0] = num2str(je->rate[0], 5, 10, i2p); | |
1761 | rate_str[1] = num2str(je->rate[1], 5, 10, i2p); | |
1762 | ||
1763 | iops_str[0] = num2str(je->iops[0], 4, 1, 0); | |
1764 | iops_str[1] = num2str(je->iops[1], 4, 1, 0); | |
1765 | ||
1766 | gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]); | |
1767 | gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]); | |
1768 | gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]); | |
1769 | gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]); | |
1770 | ||
93e2db2b JA |
1771 | graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]); |
1772 | graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]); | |
1773 | graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]); | |
1774 | graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]); | |
2fd3bb0e | 1775 | |
3e47bd25 JA |
1776 | free(rate_str[0]); |
1777 | free(rate_str[1]); | |
1778 | free(iops_str[0]); | |
1779 | free(iops_str[1]); | |
1780 | } | |
1781 | ||
1782 | if (eta_str[0]) { | |
1783 | char *dst = output + strlen(output); | |
1784 | ||
1785 | sprintf(dst, " - %s", eta_str); | |
1786 | } | |
1787 | ||
9988ca70 | 1788 | gfio_update_thread_status_all(output, perc); |
0050e5f2 | 1789 | gdk_threads_leave(); |
3e47bd25 JA |
1790 | } |
1791 | ||
a1820207 SC |
1792 | static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) |
1793 | { | |
843ad237 | 1794 | struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; |
88f6e7ad | 1795 | struct gfio_client *gc = client->client_data; |
2f99deb0 | 1796 | struct gui_entry *ge = gc->ge; |
843ad237 JA |
1797 | const char *os, *arch; |
1798 | char buf[64]; | |
1799 | ||
1800 | os = fio_get_os_string(probe->os); | |
1801 | if (!os) | |
1802 | os = "unknown"; | |
1803 | ||
1804 | arch = fio_get_arch_string(probe->arch); | |
1805 | if (!arch) | |
1806 | os = "unknown"; | |
1807 | ||
1808 | if (!client->name) | |
1809 | client->name = strdup((char *) probe->hostname); | |
1810 | ||
0050e5f2 JA |
1811 | gdk_threads_enter(); |
1812 | ||
2f99deb0 JA |
1813 | gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname); |
1814 | gtk_label_set_text(GTK_LABEL(ge->probe.os), os); | |
1815 | gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch); | |
843ad237 | 1816 | sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); |
2f99deb0 | 1817 | gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf); |
88f6e7ad | 1818 | |
85dd01e7 | 1819 | gfio_set_state(ge, GE_STATE_CONNECTED); |
0050e5f2 JA |
1820 | |
1821 | gdk_threads_leave(); | |
a1820207 SC |
1822 | } |
1823 | ||
9988ca70 JA |
1824 | static void gfio_update_thread_status(struct gui_entry *ge, |
1825 | char *status_message, double perc) | |
1826 | { | |
1827 | static char message[100]; | |
1828 | const char *m = message; | |
1829 | ||
1830 | strncpy(message, status_message, sizeof(message) - 1); | |
1831 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m); | |
1832 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0); | |
1833 | gtk_widget_queue_draw(main_ui.window); | |
1834 | } | |
1835 | ||
1836 | static void gfio_update_thread_status_all(char *status_message, double perc) | |
5b7573ab | 1837 | { |
2f99deb0 | 1838 | struct gui *ui = &main_ui; |
5b7573ab SC |
1839 | static char message[100]; |
1840 | const char *m = message; | |
1841 | ||
1842 | strncpy(message, status_message, sizeof(message) - 1); | |
2f99deb0 JA |
1843 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m); |
1844 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0); | |
1845 | gtk_widget_queue_draw(ui->window); | |
5b7573ab SC |
1846 | } |
1847 | ||
35c0ba7f | 1848 | static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd) |
3ec62ec4 | 1849 | { |
e0681f3e | 1850 | struct gfio_client *gc = client->client_data; |
3ec62ec4 | 1851 | |
0050e5f2 | 1852 | gdk_threads_enter(); |
85dd01e7 | 1853 | gfio_set_state(gc->ge, GE_STATE_NEW); |
0050e5f2 | 1854 | gdk_threads_leave(); |
3ec62ec4 JA |
1855 | } |
1856 | ||
807f9971 JA |
1857 | static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) |
1858 | { | |
1859 | struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; | |
e0681f3e | 1860 | struct gfio_client *gc = client->client_data; |
dcaeb606 | 1861 | struct thread_options *o = &gc->o; |
2f99deb0 | 1862 | struct gui_entry *ge = gc->ge; |
807f9971 | 1863 | char tmp[8]; |
807f9971 | 1864 | |
2f122b13 JA |
1865 | p->thread_number = le32_to_cpu(p->thread_number); |
1866 | p->groupid = le32_to_cpu(p->groupid); | |
dcaeb606 | 1867 | convert_thread_options_to_cpu(o, &p->top); |
807f9971 | 1868 | |
0050e5f2 JA |
1869 | gdk_threads_enter(); |
1870 | ||
2f99deb0 JA |
1871 | gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name); |
1872 | ||
3863d1ad JA |
1873 | gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name); |
1874 | gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0); | |
1875 | ||
c80b74b0 JA |
1876 | multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir)); |
1877 | multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine); | |
807f9971 | 1878 | |
dcaeb606 | 1879 | sprintf(tmp, "%u", o->iodepth); |
c80b74b0 JA |
1880 | multitext_add_entry(&ge->eta.iodepth, tmp); |
1881 | ||
1882 | multitext_set_entry(&ge->eta.iotype, 0); | |
1883 | multitext_set_entry(&ge->eta.ioengine, 0); | |
1884 | multitext_set_entry(&ge->eta.iodepth, 0); | |
0050e5f2 | 1885 | |
dcaeb606 JA |
1886 | gc->job_added++; |
1887 | ||
85dd01e7 JA |
1888 | gfio_set_state(ge, GE_STATE_JOB_SENT); |
1889 | ||
0050e5f2 | 1890 | gdk_threads_leave(); |
807f9971 JA |
1891 | } |
1892 | ||
ed727a46 JA |
1893 | static void gfio_client_timed_out(struct fio_client *client) |
1894 | { | |
e0681f3e | 1895 | struct gfio_client *gc = client->client_data; |
ed727a46 JA |
1896 | char buf[256]; |
1897 | ||
1898 | gdk_threads_enter(); | |
1899 | ||
85dd01e7 | 1900 | gfio_set_state(gc->ge, GE_STATE_NEW); |
2f99deb0 | 1901 | clear_ge_ui_info(gc->ge); |
ed727a46 JA |
1902 | |
1903 | sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); | |
16ce5adf | 1904 | show_info_dialog(gc->ge->ui, "Network timeout", buf); |
ed727a46 JA |
1905 | |
1906 | gdk_threads_leave(); | |
1907 | } | |
1908 | ||
6b79c80c JA |
1909 | static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd) |
1910 | { | |
1911 | struct gfio_client *gc = client->client_data; | |
1912 | ||
1913 | gdk_threads_enter(); | |
1914 | ||
85dd01e7 | 1915 | gfio_set_state(gc->ge, GE_STATE_JOB_DONE); |
6b79c80c JA |
1916 | |
1917 | if (gc->err_entry) | |
1918 | entry_set_int_value(gc->err_entry, client->error); | |
1919 | ||
1920 | gdk_threads_leave(); | |
1921 | } | |
1922 | ||
85dd01e7 JA |
1923 | static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd) |
1924 | { | |
1925 | struct gfio_client *gc = client->client_data; | |
1926 | ||
1927 | gdk_threads_enter(); | |
1928 | gfio_set_state(gc->ge, GE_STATE_JOB_STARTED); | |
1929 | gdk_threads_leave(); | |
1930 | } | |
1931 | ||
1932 | static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd) | |
1933 | { | |
1934 | struct gfio_client *gc = client->client_data; | |
1935 | ||
1936 | gdk_threads_enter(); | |
1937 | gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING); | |
1938 | gdk_threads_leave(); | |
1939 | } | |
1940 | ||
1b42725f JA |
1941 | static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu) |
1942 | { | |
284b1e65 | 1943 | printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples); |
1b42725f JA |
1944 | free(pdu); |
1945 | } | |
1946 | ||
a1820207 | 1947 | struct client_ops gfio_client_ops = { |
35c0ba7f | 1948 | .text = gfio_text_op, |
0420ba6a JA |
1949 | .disk_util = gfio_disk_util_op, |
1950 | .thread_status = gfio_thread_status_op, | |
1951 | .group_stats = gfio_group_stats_op, | |
2f99deb0 JA |
1952 | .jobs_eta = gfio_update_client_eta, |
1953 | .eta = gfio_update_all_eta, | |
0420ba6a | 1954 | .probe = gfio_probe_op, |
3ec62ec4 | 1955 | .quit = gfio_quit_op, |
807f9971 | 1956 | .add_job = gfio_add_job_op, |
ed727a46 | 1957 | .timed_out = gfio_client_timed_out, |
6b79c80c | 1958 | .stop = gfio_client_stop, |
85dd01e7 JA |
1959 | .start = gfio_client_start, |
1960 | .job_start = gfio_client_job_start, | |
1b42725f | 1961 | .iolog = gfio_client_iolog, |
6433ee05 | 1962 | .eta_msec = FIO_CLIENT_DEF_ETA_MSEC, |
3ec62ec4 | 1963 | .stay_connected = 1, |
46bcd498 | 1964 | .client_type = FIO_CLIENT_TYPE_GUI, |
a1820207 SC |
1965 | }; |
1966 | ||
0fd18982 JA |
1967 | /* |
1968 | * FIXME: need more handling here | |
1969 | */ | |
1970 | static void ge_destroy(struct gui_entry *ge) | |
1971 | { | |
1972 | struct gfio_client *gc = ge->client; | |
1973 | ||
1974 | if (gc && gc->client) { | |
1975 | if (ge->state >= GE_STATE_CONNECTED) | |
1976 | fio_client_terminate(gc->client); | |
1977 | ||
1978 | fio_put_client(gc->client); | |
1979 | } | |
1980 | ||
1981 | flist_del(&ge->list); | |
1982 | free(ge); | |
1983 | } | |
1984 | ||
1985 | static void ge_widget_destroy(GtkWidget *w, gpointer data) | |
1986 | { | |
0fd18982 JA |
1987 | } |
1988 | ||
1989 | static void gfio_quit(struct gui *ui) | |
1990 | { | |
1991 | struct gui_entry *ge; | |
1992 | ||
1993 | while (!flist_empty(&ui->list)) { | |
1994 | ge = flist_entry(ui->list.next, struct gui_entry, list); | |
1995 | ge_destroy(ge); | |
1996 | } | |
1997 | ||
1998 | gtk_main_quit(); | |
1999 | } | |
2000 | ||
ff1f3280 SC |
2001 | static void quit_clicked(__attribute__((unused)) GtkWidget *widget, |
2002 | __attribute__((unused)) gpointer data) | |
2003 | { | |
0fd18982 | 2004 | gfio_quit(data); |
ff1f3280 SC |
2005 | } |
2006 | ||
25927259 SC |
2007 | static void *job_thread(void *arg) |
2008 | { | |
a9eccde4 JA |
2009 | struct gui *ui = arg; |
2010 | ||
2011 | ui->handler_running = 1; | |
25927259 | 2012 | fio_handle_clients(&gfio_client_ops); |
a9eccde4 | 2013 | ui->handler_running = 0; |
25927259 SC |
2014 | return NULL; |
2015 | } | |
2016 | ||
2f99deb0 | 2017 | static int send_job_files(struct gui_entry *ge) |
60f6b330 | 2018 | { |
9988ca70 | 2019 | struct gfio_client *gc = ge->client; |
441013b4 | 2020 | int i, ret = 0; |
0420ba6a | 2021 | |
2f99deb0 | 2022 | for (i = 0; i < ge->nr_job_files; i++) { |
9988ca70 | 2023 | ret = fio_client_send_ini(gc->client, ge->job_files[i]); |
c724926b JA |
2024 | if (ret < 0) { |
2025 | GError *error; | |
2026 | ||
2027 | error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret)); | |
2028 | report_error(error); | |
2029 | g_error_free(error); | |
2030 | break; | |
2031 | } else if (ret) | |
441013b4 JA |
2032 | break; |
2033 | ||
2f99deb0 JA |
2034 | free(ge->job_files[i]); |
2035 | ge->job_files[i] = NULL; | |
441013b4 | 2036 | } |
2f99deb0 JA |
2037 | while (i < ge->nr_job_files) { |
2038 | free(ge->job_files[i]); | |
2039 | ge->job_files[i] = NULL; | |
441013b4 | 2040 | i++; |
0420ba6a JA |
2041 | } |
2042 | ||
2c77d831 JA |
2043 | free(ge->job_files); |
2044 | ge->job_files = NULL; | |
3af45201 | 2045 | ge->nr_job_files = 0; |
441013b4 | 2046 | return ret; |
60f6b330 SC |
2047 | } |
2048 | ||
63a130b7 JA |
2049 | static void *server_thread(void *arg) |
2050 | { | |
2051 | is_backend = 1; | |
2052 | gfio_server_running = 1; | |
2053 | fio_start_server(NULL); | |
2054 | gfio_server_running = 0; | |
2055 | return NULL; | |
2056 | } | |
2057 | ||
2f99deb0 | 2058 | static void gfio_start_server(void) |
63a130b7 | 2059 | { |
2f99deb0 JA |
2060 | struct gui *ui = &main_ui; |
2061 | ||
63a130b7 JA |
2062 | if (!gfio_server_running) { |
2063 | gfio_server_running = 1; | |
2064 | pthread_create(&ui->server_t, NULL, server_thread, NULL); | |
e34f6ad7 | 2065 | pthread_detach(ui->server_t); |
63a130b7 JA |
2066 | } |
2067 | } | |
2068 | ||
f3074008 | 2069 | static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, |
25927259 | 2070 | gpointer data) |
f3074008 | 2071 | { |
2f99deb0 JA |
2072 | struct gui_entry *ge = data; |
2073 | struct gfio_client *gc = ge->client; | |
25927259 | 2074 | |
78cb2fe5 JA |
2075 | if (gc) |
2076 | fio_start_client(gc->client); | |
f3074008 SC |
2077 | } |
2078 | ||
df06f220 JA |
2079 | static void file_open(GtkWidget *w, gpointer data); |
2080 | ||
2081 | static void connect_clicked(GtkWidget *widget, gpointer data) | |
3e47bd25 | 2082 | { |
2f99deb0 JA |
2083 | struct gui_entry *ge = data; |
2084 | struct gfio_client *gc = ge->client; | |
3ec62ec4 | 2085 | |
85dd01e7 | 2086 | if (ge->state == GE_STATE_NEW) { |
c724926b JA |
2087 | int ret; |
2088 | ||
2f99deb0 | 2089 | if (!ge->nr_job_files) |
cf4b0443 | 2090 | file_open(widget, ge->ui); |
2f99deb0 JA |
2091 | if (!ge->nr_job_files) |
2092 | return; | |
2093 | ||
2094 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running"); | |
2095 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
c724926b JA |
2096 | ret = fio_client_connect(gc->client); |
2097 | if (!ret) { | |
a9eccde4 JA |
2098 | if (!ge->ui->handler_running) |
2099 | pthread_create(&ge->ui->t, NULL, job_thread, ge->ui); | |
85dd01e7 | 2100 | gfio_set_state(ge, GE_STATE_CONNECTED); |
c724926b JA |
2101 | } else { |
2102 | GError *error; | |
2103 | ||
2104 | error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret)); | |
2105 | report_error(error); | |
2106 | g_error_free(error); | |
69406b92 | 2107 | } |
df06f220 | 2108 | } else { |
2f99deb0 | 2109 | fio_client_terminate(gc->client); |
85dd01e7 | 2110 | gfio_set_state(ge, GE_STATE_NEW); |
2f99deb0 | 2111 | clear_ge_ui_info(ge); |
df06f220 | 2112 | } |
3e47bd25 JA |
2113 | } |
2114 | ||
b9d2f30a JA |
2115 | static void send_clicked(GtkWidget *widget, gpointer data) |
2116 | { | |
2f99deb0 | 2117 | struct gui_entry *ge = data; |
b9d2f30a | 2118 | |
2f99deb0 | 2119 | if (send_job_files(ge)) { |
c724926b JA |
2120 | GError *error; |
2121 | ||
2122 | 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); | |
2123 | report_error(error); | |
2124 | g_error_free(error); | |
2125 | ||
2f99deb0 | 2126 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1); |
b9d2f30a | 2127 | } |
b9d2f30a JA |
2128 | } |
2129 | ||
0420ba6a JA |
2130 | static void on_info_bar_response(GtkWidget *widget, gint response, |
2131 | gpointer data) | |
2132 | { | |
2f99deb0 JA |
2133 | struct gui *ui = &main_ui; |
2134 | ||
0420ba6a JA |
2135 | if (response == GTK_RESPONSE_OK) { |
2136 | gtk_widget_destroy(widget); | |
2f99deb0 | 2137 | ui->error_info_bar = NULL; |
0420ba6a JA |
2138 | } |
2139 | } | |
2140 | ||
df06f220 | 2141 | void report_error(GError *error) |
0420ba6a | 2142 | { |
2f99deb0 JA |
2143 | struct gui *ui = &main_ui; |
2144 | ||
2145 | if (ui->error_info_bar == NULL) { | |
2146 | ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, | |
0420ba6a JA |
2147 | GTK_RESPONSE_OK, |
2148 | NULL); | |
2f99deb0 JA |
2149 | g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); |
2150 | gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar), | |
0420ba6a JA |
2151 | GTK_MESSAGE_ERROR); |
2152 | ||
2f99deb0 JA |
2153 | ui->error_label = gtk_label_new(error->message); |
2154 | GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar)); | |
2155 | gtk_container_add(GTK_CONTAINER(container), ui->error_label); | |
0420ba6a | 2156 | |
2f99deb0 JA |
2157 | gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0); |
2158 | gtk_widget_show_all(ui->vbox); | |
0420ba6a JA |
2159 | } else { |
2160 | char buffer[256]; | |
2161 | snprintf(buffer, sizeof(buffer), "Failed to open file."); | |
2f99deb0 | 2162 | gtk_label_set(GTK_LABEL(ui->error_label), buffer); |
0420ba6a JA |
2163 | } |
2164 | } | |
2165 | ||
62bc937f JA |
2166 | struct connection_widgets |
2167 | { | |
2168 | GtkWidget *hentry; | |
2169 | GtkWidget *combo; | |
2170 | GtkWidget *button; | |
2171 | }; | |
2172 | ||
2173 | static void hostname_cb(GtkEntry *entry, gpointer data) | |
2174 | { | |
2175 | struct connection_widgets *cw = data; | |
2176 | int uses_net = 0, is_localhost = 0; | |
2177 | const gchar *text; | |
2178 | gchar *ctext; | |
2179 | ||
2180 | /* | |
2181 | * Check whether to display the 'auto start backend' box | |
2182 | * or not. Show it if we are a localhost and using network, | |
2183 | * or using a socket. | |
2184 | */ | |
2185 | ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo)); | |
2186 | if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4)) | |
2187 | uses_net = 1; | |
2188 | g_free(ctext); | |
2189 | ||
2190 | if (uses_net) { | |
2191 | text = gtk_entry_get_text(GTK_ENTRY(cw->hentry)); | |
2192 | if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") || | |
2193 | !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") || | |
2194 | !strcmp(text, "ip6-loopback")) | |
2195 | is_localhost = 1; | |
2196 | } | |
2197 | ||
2198 | if (!uses_net || is_localhost) { | |
2199 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1); | |
2200 | gtk_widget_set_sensitive(cw->button, 1); | |
2201 | } else { | |
2202 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0); | |
2203 | gtk_widget_set_sensitive(cw->button, 0); | |
2204 | } | |
2205 | } | |
2206 | ||
b9f3c7ed JA |
2207 | static int get_connection_details(char **host, int *port, int *type, |
2208 | int *server_start) | |
a7a42ce1 | 2209 | { |
62bc937f JA |
2210 | GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry; |
2211 | struct connection_widgets cw; | |
a7a42ce1 JA |
2212 | char *typeentry; |
2213 | ||
2214 | dialog = gtk_dialog_new_with_buttons("Connection details", | |
2f99deb0 | 2215 | GTK_WINDOW(main_ui.window), |
a7a42ce1 JA |
2216 | GTK_DIALOG_DESTROY_WITH_PARENT, |
2217 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
2218 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); | |
2219 | ||
2220 | frame = gtk_frame_new("Hostname / socket name"); | |
f129909e JA |
2221 | /* gtk_dialog_get_content_area() is 2.14 and newer */ |
2222 | vbox = GTK_DIALOG(dialog)->vbox; | |
a7a42ce1 JA |
2223 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); |
2224 | ||
2225 | box = gtk_vbox_new(FALSE, 6); | |
2226 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2227 | ||
2228 | hbox = gtk_hbox_new(TRUE, 10); | |
2229 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
62bc937f JA |
2230 | cw.hentry = gtk_entry_new(); |
2231 | gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost"); | |
2232 | gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0); | |
a7a42ce1 JA |
2233 | |
2234 | frame = gtk_frame_new("Port"); | |
2235 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
2236 | box = gtk_vbox_new(FALSE, 10); | |
2237 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2238 | ||
2239 | hbox = gtk_hbox_new(TRUE, 4); | |
2240 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
2241 | pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); | |
2242 | ||
2243 | frame = gtk_frame_new("Type"); | |
2244 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
2245 | box = gtk_vbox_new(FALSE, 10); | |
2246 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2247 | ||
2248 | hbox = gtk_hbox_new(TRUE, 4); | |
2249 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
2250 | ||
62bc937f JA |
2251 | cw.combo = gtk_combo_box_new_text(); |
2252 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4"); | |
2253 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6"); | |
2254 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket"); | |
2255 | gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0); | |
a7a42ce1 | 2256 | |
62bc937f | 2257 | gtk_container_add(GTK_CONTAINER(hbox), cw.combo); |
a7a42ce1 | 2258 | |
b9f3c7ed JA |
2259 | frame = gtk_frame_new("Options"); |
2260 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
2261 | box = gtk_vbox_new(FALSE, 10); | |
2262 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2263 | ||
2264 | hbox = gtk_hbox_new(TRUE, 4); | |
2265 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
2266 | ||
62bc937f JA |
2267 | cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend"); |
2268 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1); | |
2269 | 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."); | |
2270 | gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6); | |
2271 | ||
2272 | /* | |
2273 | * Connect edit signal, so we can show/not-show the auto start button | |
2274 | */ | |
2275 | g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw); | |
2276 | g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw); | |
b9f3c7ed | 2277 | |
a7a42ce1 JA |
2278 | gtk_widget_show_all(dialog); |
2279 | ||
2280 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
2281 | gtk_widget_destroy(dialog); | |
2282 | return 1; | |
2283 | } | |
2284 | ||
62bc937f | 2285 | *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry))); |
a7a42ce1 JA |
2286 | *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); |
2287 | ||
62bc937f | 2288 | typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo)); |
a7a42ce1 JA |
2289 | if (!typeentry || !strncmp(typeentry, "IPv4", 4)) |
2290 | *type = Fio_client_ipv4; | |
2291 | else if (!strncmp(typeentry, "IPv6", 4)) | |
2292 | *type = Fio_client_ipv6; | |
2293 | else | |
2294 | *type = Fio_client_socket; | |
2295 | g_free(typeentry); | |
2296 | ||
62bc937f | 2297 | *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button)); |
b9f3c7ed | 2298 | |
a7a42ce1 JA |
2299 | gtk_widget_destroy(dialog); |
2300 | return 0; | |
2301 | } | |
2302 | ||
2f99deb0 | 2303 | static void gfio_client_added(struct gui_entry *ge, struct fio_client *client) |
e0681f3e JA |
2304 | { |
2305 | struct gfio_client *gc; | |
2306 | ||
2307 | gc = malloc(sizeof(*gc)); | |
2308 | memset(gc, 0, sizeof(*gc)); | |
2f99deb0 | 2309 | gc->ge = ge; |
343cb4a9 | 2310 | gc->client = fio_get_client(client); |
b9d2f30a | 2311 | |
2f99deb0 | 2312 | ge->client = gc; |
e0681f3e JA |
2313 | |
2314 | client->client_data = gc; | |
2315 | } | |
2316 | ||
2f99deb0 JA |
2317 | static GtkWidget *new_client_page(struct gui_entry *ge); |
2318 | ||
2319 | static struct gui_entry *alloc_new_gui_entry(struct gui *ui) | |
2320 | { | |
2321 | struct gui_entry *ge; | |
2322 | ||
2323 | ge = malloc(sizeof(*ge)); | |
2324 | memset(ge, 0, sizeof(*ge)); | |
85dd01e7 | 2325 | ge->state = GE_STATE_NEW; |
2f99deb0 JA |
2326 | INIT_FLIST_HEAD(&ge->list); |
2327 | flist_add_tail(&ge->list, &ui->list); | |
2328 | ge->ui = ui; | |
2329 | return ge; | |
2330 | } | |
2331 | ||
2f99deb0 JA |
2332 | static struct gui_entry *get_new_ge_with_tab(const char *name) |
2333 | { | |
2334 | struct gui_entry *ge; | |
2335 | ||
2336 | ge = alloc_new_gui_entry(&main_ui); | |
2337 | ||
2338 | ge->vbox = new_client_page(ge); | |
0fd18982 | 2339 | g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge); |
2f99deb0 JA |
2340 | |
2341 | ge->page_label = gtk_label_new(name); | |
2342 | ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label); | |
2343 | ||
2344 | gtk_widget_show_all(main_ui.window); | |
2345 | return ge; | |
2346 | } | |
2347 | ||
2348 | static void file_new(GtkWidget *w, gpointer data) | |
2349 | { | |
16ce5adf JA |
2350 | struct gui *ui = (struct gui *) data; |
2351 | struct gui_entry *ge; | |
2352 | ||
2353 | ge = get_new_ge_with_tab("Untitled"); | |
2354 | gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); | |
2f99deb0 JA |
2355 | } |
2356 | ||
2357 | /* | |
2358 | * Return the 'ge' corresponding to the tab. If the active tab is the | |
2359 | * main tab, open a new tab. | |
2360 | */ | |
38634cb1 | 2361 | static struct gui_entry *get_ge_from_page(gint cur_page, int *created) |
2f99deb0 JA |
2362 | { |
2363 | struct flist_head *entry; | |
2364 | struct gui_entry *ge; | |
2365 | ||
38634cb1 JA |
2366 | if (!cur_page) { |
2367 | if (created) | |
2368 | *created = 1; | |
2f99deb0 | 2369 | return get_new_ge_with_tab("Untitled"); |
38634cb1 JA |
2370 | } |
2371 | ||
2372 | if (created) | |
2373 | *created = 0; | |
2f99deb0 JA |
2374 | |
2375 | flist_for_each(entry, &main_ui.list) { | |
2376 | ge = flist_entry(entry, struct gui_entry, list); | |
2377 | if (ge->page_num == cur_page) | |
2378 | return ge; | |
2379 | } | |
2380 | ||
2381 | return NULL; | |
2382 | } | |
2383 | ||
85dd01e7 | 2384 | static struct gui_entry *get_ge_from_cur_tab(struct gui *ui) |
16ce5adf | 2385 | { |
16ce5adf JA |
2386 | gint cur_page; |
2387 | ||
2388 | /* | |
85dd01e7 JA |
2389 | * Main tab is tab 0, so any current page other than 0 holds |
2390 | * a ge entry. | |
16ce5adf JA |
2391 | */ |
2392 | cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); | |
85dd01e7 | 2393 | if (cur_page) |
38634cb1 | 2394 | return get_ge_from_page(cur_page, NULL); |
85dd01e7 JA |
2395 | |
2396 | return NULL; | |
2397 | } | |
2398 | ||
2399 | static void file_close(GtkWidget *w, gpointer data) | |
2400 | { | |
2401 | struct gui *ui = (struct gui *) data; | |
2402 | struct gui_entry *ge; | |
16ce5adf | 2403 | |
85dd01e7 JA |
2404 | /* |
2405 | * Can't close the main tab | |
2406 | */ | |
2407 | ge = get_ge_from_cur_tab(ui); | |
2408 | if (ge) { | |
16ce5adf JA |
2409 | gtk_widget_destroy(ge->vbox); |
2410 | return; | |
2411 | } | |
2412 | ||
f5c6726e JA |
2413 | if (!flist_empty(&ui->list)) { |
2414 | show_info_dialog(ui, "Error", "The main page view cannot be closed\n"); | |
2415 | return; | |
2416 | } | |
2417 | ||
0fd18982 | 2418 | gfio_quit(ui); |
16ce5adf JA |
2419 | } |
2420 | ||
38634cb1 JA |
2421 | static void file_add_recent(struct gui *ui, const gchar *uri) |
2422 | { | |
a217ba7d JA |
2423 | GtkRecentData grd; |
2424 | ||
2425 | memset(&grd, 0, sizeof(grd)); | |
2426 | grd.display_name = strdup("gfio"); | |
2427 | grd.description = strdup("Fio job file"); | |
2428 | grd.mime_type = strdup(GFIO_MIME); | |
2429 | grd.app_name = strdup(g_get_application_name()); | |
2430 | grd.app_exec = strdup("gfio %f/%u"); | |
2431 | ||
2432 | gtk_recent_manager_add_full(ui->recentmanager, uri, &grd); | |
38634cb1 JA |
2433 | } |
2434 | ||
2435 | static gchar *get_filename_from_uri(const gchar *uri) | |
2436 | { | |
2437 | if (strncmp(uri, "file://", 7)) | |
2438 | return strdup(uri); | |
2439 | ||
2440 | return strdup(uri + 7); | |
2441 | } | |
2442 | ||
2443 | static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host, | |
2444 | int type, int port) | |
2445 | { | |
2446 | struct fio_client *client; | |
2447 | gchar *filename; | |
2448 | ||
2449 | filename = get_filename_from_uri(uri); | |
2450 | ||
2451 | ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *)); | |
2452 | ge->job_files[ge->nr_job_files] = strdup(filename); | |
2453 | ge->nr_job_files++; | |
2454 | ||
2455 | client = fio_client_add_explicit(&gfio_client_ops, host, type, port); | |
2456 | if (!client) { | |
2457 | GError *error; | |
2458 | ||
2459 | error = g_error_new(g_quark_from_string("fio"), 1, | |
2460 | "Failed to add client %s", host); | |
2461 | report_error(error); | |
2462 | g_error_free(error); | |
2463 | return 1; | |
2464 | } | |
2465 | ||
2466 | gfio_client_added(ge, client); | |
2467 | file_add_recent(ge->ui, uri); | |
2468 | return 0; | |
2469 | } | |
2470 | ||
a6790906 | 2471 | static int do_file_open_with_tab(struct gui *ui, const gchar *uri) |
0420ba6a | 2472 | { |
a6790906 | 2473 | int port, type, server_start; |
2f99deb0 JA |
2474 | struct gui_entry *ge; |
2475 | gint cur_page; | |
38634cb1 | 2476 | char *host; |
a6790906 | 2477 | int ret, ge_is_new = 0; |
2f99deb0 JA |
2478 | |
2479 | /* | |
2480 | * Creates new tab if current tab is the main window, or the | |
2481 | * current tab already has a client. | |
2482 | */ | |
2483 | cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); | |
38634cb1 JA |
2484 | ge = get_ge_from_page(cur_page, &ge_is_new); |
2485 | if (ge->client) { | |
2f99deb0 | 2486 | ge = get_new_ge_with_tab("Untitled"); |
38634cb1 JA |
2487 | ge_is_new = 1; |
2488 | } | |
2f99deb0 JA |
2489 | |
2490 | gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); | |
0420ba6a | 2491 | |
a6790906 JA |
2492 | if (get_connection_details(&host, &port, &type, &server_start)) { |
2493 | if (ge_is_new) | |
2494 | gtk_widget_destroy(ge->vbox); | |
2495 | ||
2496 | return 1; | |
2497 | } | |
2498 | ||
2499 | ret = do_file_open(ge, uri, host, type, port); | |
2500 | ||
2501 | free(host); | |
2502 | ||
2503 | if (!ret) { | |
2504 | if (server_start) | |
2505 | gfio_start_server(); | |
2506 | } else { | |
2507 | if (ge_is_new) | |
2508 | gtk_widget_destroy(ge->vbox); | |
2509 | } | |
2510 | ||
2511 | return ret; | |
2512 | } | |
2513 | ||
2514 | static void recent_open(GtkAction *action, gpointer data) | |
2515 | { | |
2516 | struct gui *ui = (struct gui *) data; | |
2517 | GtkRecentInfo *info; | |
2518 | const gchar *uri; | |
2519 | ||
2520 | info = g_object_get_data(G_OBJECT(action), "gtk-recent-info"); | |
2521 | uri = gtk_recent_info_get_uri(info); | |
2522 | ||
2523 | do_file_open_with_tab(ui, uri); | |
2524 | } | |
2525 | ||
2526 | static void file_open(GtkWidget *w, gpointer data) | |
2527 | { | |
2528 | struct gui *ui = data; | |
2529 | GtkWidget *dialog; | |
2530 | GSList *filenames, *fn_glist; | |
2531 | GtkFileFilter *filter; | |
2532 | ||
0420ba6a | 2533 | dialog = gtk_file_chooser_dialog_new("Open File", |
63a130b7 | 2534 | GTK_WINDOW(ui->window), |
0420ba6a JA |
2535 | GTK_FILE_CHOOSER_ACTION_OPEN, |
2536 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
2537 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
2538 | NULL); | |
2539 | gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | |
2540 | ||
2541 | filter = gtk_file_filter_new(); | |
2542 | gtk_file_filter_add_pattern(filter, "*.fio"); | |
2543 | gtk_file_filter_add_pattern(filter, "*.job"); | |
2d262990 | 2544 | gtk_file_filter_add_pattern(filter, "*.ini"); |
38634cb1 | 2545 | gtk_file_filter_add_mime_type(filter, GFIO_MIME); |
0420ba6a JA |
2546 | gtk_file_filter_set_name(filter, "Fio job file"); |
2547 | gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); | |
2548 | ||
2549 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
2550 | gtk_widget_destroy(dialog); | |
2551 | return; | |
2552 | } | |
2553 | ||
2554 | fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | |
a7a42ce1 JA |
2555 | |
2556 | gtk_widget_destroy(dialog); | |
2557 | ||
0420ba6a JA |
2558 | filenames = fn_glist; |
2559 | while (filenames != NULL) { | |
a6790906 JA |
2560 | if (do_file_open_with_tab(ui, filenames->data)) |
2561 | break; | |
0420ba6a JA |
2562 | filenames = g_slist_next(filenames); |
2563 | } | |
63a130b7 | 2564 | |
0420ba6a | 2565 | g_slist_free(fn_glist); |
0420ba6a JA |
2566 | } |
2567 | ||
2568 | static void file_save(GtkWidget *w, gpointer data) | |
2569 | { | |
63a130b7 | 2570 | struct gui *ui = data; |
0420ba6a JA |
2571 | GtkWidget *dialog; |
2572 | ||
2573 | dialog = gtk_file_chooser_dialog_new("Save File", | |
63a130b7 | 2574 | GTK_WINDOW(ui->window), |
0420ba6a JA |
2575 | GTK_FILE_CHOOSER_ACTION_SAVE, |
2576 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
2577 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
2578 | NULL); | |
2579 | ||
2580 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); | |
2581 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); | |
2582 | ||
2583 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | |
2584 | char *filename; | |
2585 | ||
2586 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
2587 | // save_job_file(filename); | |
2588 | g_free(filename); | |
2589 | } | |
2590 | gtk_widget_destroy(dialog); | |
2591 | } | |
2592 | ||
9b260bdf JA |
2593 | static void view_log_destroy(GtkWidget *w, gpointer data) |
2594 | { | |
2595 | struct gui *ui = (struct gui *) data; | |
2596 | ||
2597 | gtk_widget_ref(ui->log_tree); | |
2598 | gtk_container_remove(GTK_CONTAINER(w), ui->log_tree); | |
2599 | gtk_widget_destroy(w); | |
4cbe7211 | 2600 | ui->log_view = NULL; |
9b260bdf JA |
2601 | } |
2602 | ||
2603 | static void view_log(GtkWidget *w, gpointer data) | |
2604 | { | |
4cbe7211 JA |
2605 | GtkWidget *win, *scroll, *vbox, *box; |
2606 | struct gui *ui = (struct gui *) data; | |
9b260bdf | 2607 | |
4cbe7211 JA |
2608 | if (ui->log_view) |
2609 | return; | |
2610 | ||
2611 | ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
9b260bdf | 2612 | gtk_window_set_title(GTK_WINDOW(win), "Log"); |
4cbe7211 | 2613 | gtk_window_set_default_size(GTK_WINDOW(win), 700, 500); |
9b260bdf | 2614 | |
4cbe7211 JA |
2615 | scroll = gtk_scrolled_window_new(NULL, NULL); |
2616 | ||
2617 | gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); | |
2618 | ||
2619 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2620 | ||
2621 | box = gtk_hbox_new(TRUE, 0); | |
2622 | gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree); | |
2623 | g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui); | |
2624 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box); | |
2625 | ||
2626 | vbox = gtk_vbox_new(TRUE, 5); | |
2627 | gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll); | |
9b260bdf | 2628 | |
4cbe7211 | 2629 | gtk_container_add(GTK_CONTAINER(win), vbox); |
9b260bdf JA |
2630 | gtk_widget_show_all(win); |
2631 | } | |
2632 | ||
85dd01e7 JA |
2633 | static void connect_job_entry(GtkWidget *w, gpointer data) |
2634 | { | |
2635 | struct gui *ui = (struct gui *) data; | |
2636 | struct gui_entry *ge; | |
2637 | ||
2638 | ge = get_ge_from_cur_tab(ui); | |
2639 | if (ge) | |
2640 | connect_clicked(w, ge); | |
2641 | } | |
2642 | ||
2643 | static void send_job_entry(GtkWidget *w, gpointer data) | |
2644 | { | |
2645 | struct gui *ui = (struct gui *) data; | |
2646 | struct gui_entry *ge; | |
2647 | ||
2648 | ge = get_ge_from_cur_tab(ui); | |
2649 | if (ge) | |
2650 | send_clicked(w, ge); | |
2651 | ||
2652 | } | |
2653 | ||
2654 | static void edit_job_entry(GtkWidget *w, gpointer data) | |
16ce5adf JA |
2655 | { |
2656 | } | |
2657 | ||
85dd01e7 JA |
2658 | static void start_job_entry(GtkWidget *w, gpointer data) |
2659 | { | |
2660 | struct gui *ui = (struct gui *) data; | |
2661 | struct gui_entry *ge; | |
2662 | ||
2663 | ge = get_ge_from_cur_tab(ui); | |
2664 | if (ge) | |
2665 | start_job_clicked(w, ge); | |
2666 | } | |
2667 | ||
781ccba6 JA |
2668 | static void view_results(GtkWidget *w, gpointer data) |
2669 | { | |
2670 | struct gui *ui = (struct gui *) data; | |
2671 | struct gfio_client *gc; | |
2672 | struct gui_entry *ge; | |
2673 | ||
2674 | ge = get_ge_from_cur_tab(ui); | |
2675 | if (!ge) | |
2676 | return; | |
2677 | ||
2678 | if (ge->results_window) | |
2679 | return; | |
2680 | ||
2681 | gc = ge->client; | |
2682 | if (gc && gc->nr_results) | |
2683 | gfio_display_end_results(gc); | |
2684 | } | |
2685 | ||
8577f4fd JA |
2686 | static void __update_graph_limits(struct gfio_graphs *g) |
2687 | { | |
2688 | line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit); | |
2689 | line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit); | |
2690 | } | |
2691 | ||
2692 | static void update_graph_limits(void) | |
2693 | { | |
2694 | struct flist_head *entry; | |
2695 | struct gui_entry *ge; | |
2696 | ||
2697 | __update_graph_limits(&main_ui.graphs); | |
2698 | ||
2699 | flist_for_each(entry, &main_ui.list) { | |
2700 | ge = flist_entry(entry, struct gui_entry, list); | |
2701 | __update_graph_limits(&ge->graphs); | |
2702 | } | |
2703 | } | |
2704 | ||
46974a7d JA |
2705 | static void preferences(GtkWidget *w, gpointer data) |
2706 | { | |
f3e8440f | 2707 | GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font; |
1cf6bca5 | 2708 | GtkWidget *hbox, *spin, *entry, *spin_int; |
46974a7d JA |
2709 | int i; |
2710 | ||
2711 | dialog = gtk_dialog_new_with_buttons("Preferences", | |
2f99deb0 | 2712 | GTK_WINDOW(main_ui.window), |
46974a7d JA |
2713 | GTK_DIALOG_DESTROY_WITH_PARENT, |
2714 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
2715 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, | |
2716 | NULL); | |
2717 | ||
8577f4fd | 2718 | frame = gtk_frame_new("Graphing"); |
f3e8440f JA |
2719 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); |
2720 | vbox = gtk_vbox_new(FALSE, 6); | |
2721 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
2722 | ||
1cf6bca5 JA |
2723 | hbox = gtk_hbox_new(FALSE, 5); |
2724 | gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); | |
2725 | entry = gtk_label_new("Font face to use for graph labels"); | |
2726 | gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5); | |
2727 | ||
f3e8440f | 2728 | font = gtk_font_button_new(); |
1cf6bca5 | 2729 | gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5); |
f3e8440f | 2730 | |
8577f4fd JA |
2731 | box = gtk_vbox_new(FALSE, 6); |
2732 | gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); | |
2733 | ||
2734 | hbox = gtk_hbox_new(FALSE, 5); | |
1cf6bca5 | 2735 | gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5); |
8577f4fd JA |
2736 | entry = gtk_label_new("Maximum number of data points in graph (seconds)"); |
2737 | gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5); | |
2738 | ||
c05d9056 | 2739 | spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit); |
8577f4fd | 2740 | |
1cf6bca5 JA |
2741 | box = gtk_vbox_new(FALSE, 6); |
2742 | gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); | |
2743 | ||
2744 | hbox = gtk_hbox_new(FALSE, 5); | |
2745 | gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5); | |
2746 | entry = gtk_label_new("Client ETA request interval (msec)"); | |
2747 | gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5); | |
2748 | ||
2749 | spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec); | |
a31d9fa4 JA |
2750 | frame = gtk_frame_new("Debug logging"); |
2751 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); | |
2752 | vbox = gtk_vbox_new(FALSE, 6); | |
2753 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
2754 | ||
2755 | box = gtk_hbox_new(FALSE, 6); | |
2756 | gtk_container_add(GTK_CONTAINER(vbox), box); | |
2757 | ||
2758 | buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); | |
2759 | ||
2760 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
2761 | if (i == 7) { | |
2762 | box = gtk_hbox_new(FALSE, 6); | |
2763 | gtk_container_add(GTK_CONTAINER(vbox), box); | |
2764 | } | |
2765 | ||
2766 | ||
2767 | buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); | |
2768 | gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); | |
2769 | gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); | |
2770 | } | |
2771 | ||
46974a7d JA |
2772 | gtk_widget_show_all(dialog); |
2773 | ||
2774 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
2775 | gtk_widget_destroy(dialog); | |
2776 | return; | |
2777 | } | |
2778 | ||
2779 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
2780 | int set; | |
2781 | ||
2782 | set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); | |
2783 | if (set) | |
2784 | fio_debug |= (1UL << i); | |
2785 | } | |
2786 | ||
f3e8440f | 2787 | gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); |
8577f4fd JA |
2788 | gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); |
2789 | update_graph_limits(); | |
1cf6bca5 | 2790 | gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int)); |
8577f4fd | 2791 | |
46974a7d JA |
2792 | gtk_widget_destroy(dialog); |
2793 | } | |
2794 | ||
0420ba6a JA |
2795 | static void about_dialog(GtkWidget *w, gpointer data) |
2796 | { | |
81e4ea6e JA |
2797 | const char *authors[] = { |
2798 | "Jens Axboe <axboe@kernel.dk>", | |
2799 | "Stephen Carmeron <stephenmcameron@gmail.com>", | |
2800 | NULL | |
2801 | }; | |
84a72ed3 JA |
2802 | const char *license[] = { |
2803 | "Fio is free software; you can redistribute it and/or modify " | |
2804 | "it under the terms of the GNU General Public License as published by " | |
2805 | "the Free Software Foundation; either version 2 of the License, or " | |
2806 | "(at your option) any later version.\n", | |
2807 | "Fio is distributed in the hope that it will be useful, " | |
2808 | "but WITHOUT ANY WARRANTY; without even the implied warranty of " | |
2809 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " | |
2810 | "GNU General Public License for more details.\n", | |
2811 | "You should have received a copy of the GNU General Public License " | |
2812 | "along with Fio; if not, write to the Free Software Foundation, Inc., " | |
2813 | "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" | |
2814 | }; | |
2815 | char *license_trans; | |
2816 | ||
2817 | license_trans = g_strconcat(license[0], "\n", license[1], "\n", | |
2818 | license[2], "\n", NULL); | |
81e4ea6e | 2819 | |
0420ba6a JA |
2820 | gtk_show_about_dialog(NULL, |
2821 | "program-name", "gfio", | |
2822 | "comments", "Gtk2 UI for fio", | |
84a72ed3 | 2823 | "license", license_trans, |
81e4ea6e JA |
2824 | "website", "http://git.kernel.dk/?p=fio.git;a=summary", |
2825 | "authors", authors, | |
0420ba6a | 2826 | "version", fio_version_string, |
81e4ea6e | 2827 | "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>", |
0420ba6a JA |
2828 | "logo-icon-name", "fio", |
2829 | /* Must be last: */ | |
81e4ea6e | 2830 | "wrap-license", TRUE, |
0420ba6a | 2831 | NULL); |
84a72ed3 | 2832 | |
2f99deb0 | 2833 | g_free(license_trans); |
0420ba6a JA |
2834 | } |
2835 | ||
2836 | static GtkActionEntry menu_items[] = { | |
46974a7d | 2837 | { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, |
9b260bdf | 2838 | { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL}, |
16ce5adf | 2839 | { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL}, |
46974a7d | 2840 | { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, |
2f99deb0 | 2841 | { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) }, |
16ce5adf | 2842 | { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) }, |
46974a7d JA |
2843 | { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, |
2844 | { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, | |
2845 | { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, | |
9b260bdf | 2846 | { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) }, |
781ccba6 | 2847 | { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) }, |
85dd01e7 JA |
2848 | { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) }, |
2849 | { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) }, | |
2850 | { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) }, | |
2851 | { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) }, | |
46974a7d JA |
2852 | { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, |
2853 | { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, | |
0420ba6a | 2854 | }; |
3e47bd25 | 2855 | static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); |
0420ba6a JA |
2856 | |
2857 | static const gchar *ui_string = " \ | |
2858 | <ui> \ | |
2859 | <menubar name=\"MainMenu\"> \ | |
2860 | <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ | |
2f99deb0 | 2861 | <menuitem name=\"New\" action=\"NewFile\" /> \ |
bf64138b | 2862 | <menuitem name=\"Open\" action=\"OpenFile\" /> \ |
16ce5adf | 2863 | <menuitem name=\"Close\" action=\"CloseFile\" /> \ |
2f99deb0 | 2864 | <separator name=\"Separator1\"/> \ |
0420ba6a | 2865 | <menuitem name=\"Save\" action=\"SaveFile\" /> \ |
46974a7d | 2866 | <separator name=\"Separator2\"/> \ |
2f99deb0 JA |
2867 | <menuitem name=\"Preferences\" action=\"Preferences\" /> \ |
2868 | <separator name=\"Separator3\"/> \ | |
261f21d0 JA |
2869 | <placeholder name=\"FileRecentFiles\"/> \ |
2870 | <separator name=\"Separator4\"/> \ | |
0420ba6a JA |
2871 | <menuitem name=\"Quit\" action=\"Quit\" /> \ |
2872 | </menu> \ | |
16ce5adf | 2873 | <menu name=\"JobMenu\" action=\"JobMenuAction\"> \ |
85dd01e7 | 2874 | <menuitem name=\"Connect\" action=\"ConnectJob\" /> \ |
261f21d0 | 2875 | <separator name=\"Separator5\"/> \ |
85dd01e7 JA |
2876 | <menuitem name=\"Edit job\" action=\"EditJob\" /> \ |
2877 | <menuitem name=\"Send job\" action=\"SendJob\" /> \ | |
261f21d0 | 2878 | <separator name=\"Separator6\"/> \ |
85dd01e7 | 2879 | <menuitem name=\"Start job\" action=\"StartJob\" /> \ |
16ce5adf | 2880 | </menu>\ |
9b260bdf | 2881 | <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \ |
781ccba6 JA |
2882 | <menuitem name=\"Results\" action=\"ViewResults\" /> \ |
2883 | <separator name=\"Separator7\"/> \ | |
9b260bdf JA |
2884 | <menuitem name=\"Log\" action=\"ViewLog\" /> \ |
2885 | </menu>\ | |
0420ba6a JA |
2886 | <menu name=\"Help\" action=\"HelpMenuAction\"> \ |
2887 | <menuitem name=\"About\" action=\"About\" /> \ | |
2888 | </menu> \ | |
2889 | </menubar> \ | |
2890 | </ui> \ | |
2891 | "; | |
2892 | ||
4cbe7211 JA |
2893 | static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager, |
2894 | struct gui *ui) | |
0420ba6a | 2895 | { |
ca664f49 | 2896 | GtkActionGroup *action_group; |
0420ba6a JA |
2897 | GError *error = 0; |
2898 | ||
2899 | action_group = gtk_action_group_new("Menu"); | |
4cbe7211 | 2900 | gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui); |
0420ba6a JA |
2901 | |
2902 | gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); | |
2903 | gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); | |
2904 | ||
2905 | gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); | |
02421e69 | 2906 | |
0420ba6a JA |
2907 | return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); |
2908 | } | |
2909 | ||
2910 | void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, | |
2911 | GtkWidget *vbox, GtkUIManager *ui_manager) | |
2912 | { | |
2913 | gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); | |
2914 | } | |
2915 | ||
c80b74b0 JA |
2916 | static void combo_entry_changed(GtkComboBox *box, gpointer data) |
2917 | { | |
2918 | struct gui_entry *ge = (struct gui_entry *) data; | |
2919 | gint index; | |
2920 | ||
2921 | index = gtk_combo_box_get_active(box); | |
2922 | ||
2923 | multitext_set_entry(&ge->eta.iotype, index); | |
2924 | multitext_set_entry(&ge->eta.ioengine, index); | |
2925 | multitext_set_entry(&ge->eta.iodepth, index); | |
2926 | } | |
2927 | ||
2928 | static void combo_entry_destroy(GtkWidget *widget, gpointer data) | |
2929 | { | |
2930 | struct gui_entry *ge = (struct gui_entry *) data; | |
2931 | ||
2932 | multitext_free(&ge->eta.iotype); | |
2933 | multitext_free(&ge->eta.ioengine); | |
2934 | multitext_free(&ge->eta.iodepth); | |
2935 | } | |
2936 | ||
2f99deb0 | 2937 | static GtkWidget *new_client_page(struct gui_entry *ge) |
ff1f3280 | 2938 | { |
2f99deb0 | 2939 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; |
65476336 | 2940 | GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox; |
0420ba6a | 2941 | |
2f99deb0 | 2942 | main_vbox = gtk_vbox_new(FALSE, 3); |
45032dd8 | 2943 | |
65476336 JA |
2944 | top_align = gtk_alignment_new(0, 0, 1, 0); |
2945 | top_vbox = gtk_vbox_new(FALSE, 3); | |
2946 | gtk_container_add(GTK_CONTAINER(top_align), top_vbox); | |
2947 | gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0); | |
2f99deb0 JA |
2948 | |
2949 | probe = gtk_frame_new("Job"); | |
2950 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
2951 | probe_frame = gtk_vbox_new(FALSE, 3); | |
2952 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
2953 | ||
2954 | probe_box = gtk_hbox_new(FALSE, 3); | |
2955 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2956 | ge->probe.hostname = new_info_label_in_frame(probe_box, "Host"); | |
2957 | ge->probe.os = new_info_label_in_frame(probe_box, "OS"); | |
2958 | ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); | |
2959 | ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); | |
2960 | ||
2961 | probe_box = gtk_hbox_new(FALSE, 3); | |
2962 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2963 | ||
3863d1ad | 2964 | ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs"); |
c80b74b0 JA |
2965 | g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge); |
2966 | g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge); | |
2967 | ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO"); | |
2968 | ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine"); | |
2969 | ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth"); | |
2f99deb0 JA |
2970 | ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs"); |
2971 | ge->eta.files = new_info_entry_in_frame(probe_box, "Open files"); | |
2972 | ||
2973 | probe_box = gtk_hbox_new(FALSE, 3); | |
2974 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2975 | ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); | |
2976 | ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
2977 | ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); | |
2978 | ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
2979 | ||
2980 | /* | |
2981 | * Only add this if we have a commit rate | |
2839f0c6 | 2982 | */ |
2f99deb0 JA |
2983 | #if 0 |
2984 | probe_box = gtk_hbox_new(FALSE, 3); | |
2985 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
2839f0c6 | 2986 | |
2f99deb0 JA |
2987 | ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
2988 | ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
ff1f3280 | 2989 | |
2f99deb0 JA |
2990 | ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
2991 | ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
2992 | #endif | |
ff1f3280 | 2993 | |
2f99deb0 JA |
2994 | /* |
2995 | * Set up a drawing area and IOPS and bandwidth graphs | |
2996 | */ | |
2f99deb0 | 2997 | ge->graphs.drawing_area = gtk_drawing_area_new(); |
2f99deb0 | 2998 | gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area), |
57f9d28e | 2999 | DRAWING_AREA_XDIM, DRAWING_AREA_YDIM); |
2f99deb0 JA |
3000 | gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white); |
3001 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event", | |
3002 | G_CALLBACK(on_expose_drawing_area), &ge->graphs); | |
3003 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event", | |
3004 | G_CALLBACK(on_config_drawing_area), &ge->graphs); | |
65476336 JA |
3005 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
3006 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | |
2f99deb0 | 3007 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
65476336 | 3008 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), |
2f99deb0 | 3009 | ge->graphs.drawing_area); |
65476336 | 3010 | gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0); |
04cc6b77 | 3011 | |
2f99deb0 JA |
3012 | setup_graphs(&ge->graphs); |
3013 | ||
3014 | /* | |
3015 | * Set up alignments for widgets at the bottom of ui, | |
3016 | * align bottom left, expand horizontally but not vertically | |
3017 | */ | |
65476336 | 3018 | bottom_align = gtk_alignment_new(0, 1, 1, 0); |
2f99deb0 | 3019 | ge->buttonbox = gtk_hbox_new(FALSE, 0); |
65476336 JA |
3020 | gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox); |
3021 | gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0); | |
2f99deb0 JA |
3022 | |
3023 | add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist)); | |
0420ba6a | 3024 | |
c36f98d9 | 3025 | /* |
2f99deb0 JA |
3026 | * Set up thread status progress bar |
3027 | */ | |
3028 | ge->thread_status_pb = gtk_progress_bar_new(); | |
3029 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
3030 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections"); | |
3031 | gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb); | |
3032 | ||
3033 | ||
3034 | return main_vbox; | |
3035 | } | |
3036 | ||
3037 | static GtkWidget *new_main_page(struct gui *ui) | |
3038 | { | |
3039 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; | |
65476336 | 3040 | GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox; |
2f99deb0 JA |
3041 | |
3042 | main_vbox = gtk_vbox_new(FALSE, 3); | |
3043 | ||
3044 | /* | |
3045 | * Set up alignments for widgets at the top of ui, | |
c36f98d9 SC |
3046 | * align top left, expand horizontally but not vertically |
3047 | */ | |
65476336 JA |
3048 | top_align = gtk_alignment_new(0, 0, 1, 0); |
3049 | top_vbox = gtk_vbox_new(FALSE, 0); | |
3050 | gtk_container_add(GTK_CONTAINER(top_align), top_vbox); | |
3051 | gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0); | |
c36f98d9 | 3052 | |
2f99deb0 JA |
3053 | probe = gtk_frame_new("Run statistics"); |
3054 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
843ad237 JA |
3055 | probe_frame = gtk_vbox_new(FALSE, 3); |
3056 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
3057 | ||
3058 | probe_box = gtk_hbox_new(FALSE, 3); | |
2f99deb0 | 3059 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); |
3863d1ad | 3060 | ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running"); |
ca850992 JA |
3061 | ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); |
3062 | ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
3063 | ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); | |
3064 | ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
3e47bd25 | 3065 | |
807f9971 JA |
3066 | /* |
3067 | * Only add this if we have a commit rate | |
3068 | */ | |
3069 | #if 0 | |
3e47bd25 JA |
3070 | probe_box = gtk_hbox_new(FALSE, 3); |
3071 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
807f9971 JA |
3072 | |
3073 | ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); | |
3074 | ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
3075 | ||
3e47bd25 JA |
3076 | ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
3077 | ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
807f9971 | 3078 | #endif |
3e47bd25 | 3079 | |
736f2dff | 3080 | /* |
2fd3bb0e | 3081 | * Set up a drawing area and IOPS and bandwidth graphs |
736f2dff | 3082 | */ |
2f99deb0 | 3083 | ui->graphs.drawing_area = gtk_drawing_area_new(); |
2f99deb0 | 3084 | gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area), |
57f9d28e | 3085 | DRAWING_AREA_XDIM, DRAWING_AREA_YDIM); |
2f99deb0 JA |
3086 | gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white); |
3087 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event", | |
3088 | G_CALLBACK(on_expose_drawing_area), &ui->graphs); | |
3089 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event", | |
3090 | G_CALLBACK(on_config_drawing_area), &ui->graphs); | |
65476336 JA |
3091 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
3092 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | |
736f2dff | 3093 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
65476336 | 3094 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), |
2f99deb0 | 3095 | ui->graphs.drawing_area); |
65476336 | 3096 | gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, |
e164534f | 3097 | TRUE, TRUE, 0); |
736f2dff | 3098 | |
2f99deb0 | 3099 | setup_graphs(&ui->graphs); |
2fd3bb0e | 3100 | |
c36f98d9 SC |
3101 | /* |
3102 | * Set up alignments for widgets at the bottom of ui, | |
3103 | * align bottom left, expand horizontally but not vertically | |
3104 | */ | |
65476336 | 3105 | bottom_align = gtk_alignment_new(0, 1, 1, 0); |
c36f98d9 | 3106 | ui->buttonbox = gtk_hbox_new(FALSE, 0); |
65476336 JA |
3107 | gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox); |
3108 | gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0); | |
c36f98d9 | 3109 | |
3ec62ec4 JA |
3110 | /* |
3111 | * Set up thread status progress bar | |
3112 | */ | |
3113 | ui->thread_status_pb = gtk_progress_bar_new(); | |
3114 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); | |
8663ea65 | 3115 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); |
3ec62ec4 JA |
3116 | gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); |
3117 | ||
2f99deb0 JA |
3118 | return main_vbox; |
3119 | } | |
3120 | ||
3121 | static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget, | |
3122 | guint page, gpointer data) | |
3123 | ||
3124 | { | |
02421e69 | 3125 | struct gui *ui = (struct gui *) data; |
85dd01e7 JA |
3126 | struct gui_entry *ge; |
3127 | ||
3128 | if (!page) { | |
3129 | set_job_menu_visible(ui, 0); | |
781ccba6 | 3130 | set_view_results_visible(ui, 0); |
85dd01e7 JA |
3131 | return TRUE; |
3132 | } | |
3133 | ||
3134 | set_job_menu_visible(ui, 1); | |
38634cb1 | 3135 | ge = get_ge_from_page(page, NULL); |
85dd01e7 JA |
3136 | if (ge) |
3137 | update_button_states(ui, ge); | |
02421e69 | 3138 | |
2f99deb0 JA |
3139 | return TRUE; |
3140 | } | |
3141 | ||
38634cb1 JA |
3142 | static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b) |
3143 | { | |
3144 | time_t time_a = gtk_recent_info_get_visited(a); | |
3145 | time_t time_b = gtk_recent_info_get_visited(b); | |
3146 | ||
3147 | return time_b - time_a; | |
3148 | } | |
3149 | ||
3150 | static void add_recent_file_items(struct gui *ui) | |
3151 | { | |
3152 | const gchar *gfio = g_get_application_name(); | |
3153 | GList *items, *item; | |
3154 | int i = 0; | |
3155 | ||
3156 | if (ui->recent_ui_id) { | |
3157 | gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id); | |
3158 | gtk_ui_manager_ensure_update(ui->uimanager); | |
3159 | } | |
3160 | ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager); | |
3161 | ||
3162 | if (ui->actiongroup) { | |
3163 | gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup); | |
3164 | g_object_unref(ui->actiongroup); | |
3165 | } | |
3166 | ui->actiongroup = gtk_action_group_new("RecentFileActions"); | |
3167 | ||
3168 | gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1); | |
3169 | ||
3170 | items = gtk_recent_manager_get_items(ui->recentmanager); | |
3171 | items = g_list_sort(items, (GCompareFunc) compare_recent_items); | |
3172 | ||
3173 | for (item = items; item && item->data; item = g_list_next(item)) { | |
3174 | GtkRecentInfo *info = (GtkRecentInfo *) item->data; | |
3175 | gchar *action_name; | |
3176 | const gchar *label; | |
3177 | GtkAction *action; | |
3178 | ||
3179 | if (!gtk_recent_info_has_application(info, gfio)) | |
3180 | continue; | |
3181 | ||
3182 | /* | |
3183 | * We only support local files for now | |
3184 | */ | |
3185 | if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info)) | |
3186 | continue; | |
3187 | ||
3188 | action_name = g_strdup_printf("RecentFile%u", i++); | |
3189 | label = gtk_recent_info_get_display_name(info); | |
3190 | ||
3191 | action = g_object_new(GTK_TYPE_ACTION, | |
3192 | "name", action_name, | |
3193 | "label", label, NULL); | |
3194 | ||
3195 | g_object_set_data_full(G_OBJECT(action), "gtk-recent-info", | |
3196 | gtk_recent_info_ref(info), | |
3197 | (GDestroyNotify) gtk_recent_info_unref); | |
3198 | ||
3199 | ||
3200 | g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui); | |
3201 | ||
3202 | gtk_action_group_add_action(ui->actiongroup, action); | |
3203 | g_object_unref(action); | |
3204 | ||
3205 | gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id, | |
3206 | "/MainMenu/FileMenu/FileRecentFiles", | |
3207 | label, action_name, | |
3208 | GTK_UI_MANAGER_MENUITEM, FALSE); | |
3209 | ||
3210 | g_free(action_name); | |
3211 | ||
3212 | if (i == 8) | |
3213 | break; | |
3214 | } | |
3215 | ||
3216 | g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL); | |
3217 | g_list_free(items); | |
3218 | } | |
3219 | ||
a6790906 JA |
3220 | static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx, |
3221 | gint x, gint y, GtkSelectionData *data, | |
3222 | guint info, guint time) | |
3223 | { | |
3224 | struct gui *ui = &main_ui; | |
3225 | gchar **uris; | |
3226 | GtkWidget *source; | |
3227 | int i; | |
3228 | ||
3229 | source = gtk_drag_get_source_widget(ctx); | |
3230 | if (source && widget == gtk_widget_get_toplevel(source)) { | |
3231 | gtk_drag_finish(ctx, FALSE, FALSE, time); | |
3232 | return; | |
3233 | } | |
3234 | ||
3235 | uris = gtk_selection_data_get_uris(data); | |
3236 | if (!uris) { | |
3237 | gtk_drag_finish(ctx, FALSE, FALSE, time); | |
3238 | return; | |
3239 | } | |
3240 | ||
3241 | i = 0; | |
3242 | while (uris[i]) { | |
3243 | if (do_file_open_with_tab(ui, uris[i])) | |
3244 | break; | |
3245 | i++; | |
3246 | } | |
3247 | ||
3248 | gtk_drag_finish(ctx, TRUE, FALSE, time); | |
3249 | g_strfreev(uris); | |
3250 | } | |
3251 | ||
2f99deb0 JA |
3252 | static void init_ui(int *argc, char **argv[], struct gui *ui) |
3253 | { | |
3254 | GtkSettings *settings; | |
02421e69 | 3255 | GtkWidget *vbox; |
2f99deb0 JA |
3256 | |
3257 | /* Magical g*thread incantation, you just need this thread stuff. | |
3258 | * Without it, the update that happens in gfio_update_thread_status | |
3259 | * doesn't really happen in a timely fashion, you need expose events | |
3260 | */ | |
3261 | if (!g_thread_supported()) | |
3262 | g_thread_init(NULL); | |
3263 | gdk_threads_init(); | |
3264 | ||
3265 | gtk_init(argc, argv); | |
3266 | settings = gtk_settings_get_default(); | |
3267 | gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); | |
3268 | g_type_init(); | |
814479d5 | 3269 | gdk_color_parse("white", &white); |
2f99deb0 JA |
3270 | |
3271 | ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
814479d5 | 3272 | gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); |
2f99deb0 JA |
3273 | gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768); |
3274 | ||
3275 | g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL); | |
3276 | g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL); | |
3277 | ||
3278 | ui->vbox = gtk_vbox_new(FALSE, 0); | |
3279 | gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox); | |
3280 | ||
02421e69 JA |
3281 | ui->uimanager = gtk_ui_manager_new(); |
3282 | ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui); | |
3283 | gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager); | |
2f99deb0 | 3284 | |
38634cb1 JA |
3285 | ui->recentmanager = gtk_recent_manager_get_default(); |
3286 | add_recent_file_items(ui); | |
3287 | ||
2f99deb0 JA |
3288 | ui->notebook = gtk_notebook_new(); |
3289 | g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui); | |
b870c31b | 3290 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1); |
0aa928c4 | 3291 | gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook)); |
2f99deb0 JA |
3292 | gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook); |
3293 | ||
3294 | vbox = new_main_page(ui); | |
a6790906 JA |
3295 | gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY); |
3296 | gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window)); | |
3297 | g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui); | |
2f99deb0 JA |
3298 | |
3299 | gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main")); | |
3300 | ||
9b260bdf | 3301 | gfio_ui_setup_log(ui); |
3ec62ec4 | 3302 | |
ff1f3280 SC |
3303 | gtk_widget_show_all(ui->window); |
3304 | } | |
3305 | ||
8232e285 | 3306 | int main(int argc, char *argv[], char *envp[]) |
ff1f3280 | 3307 | { |
8232e285 SC |
3308 | if (initialize_fio(envp)) |
3309 | return 1; | |
0420ba6a JA |
3310 | if (fio_init_options()) |
3311 | return 1; | |
a1820207 | 3312 | |
2f99deb0 JA |
3313 | memset(&main_ui, 0, sizeof(main_ui)); |
3314 | INIT_FLIST_HEAD(&main_ui.list); | |
3315 | ||
3316 | init_ui(&argc, &argv, &main_ui); | |
5b7573ab | 3317 | |
2839f0c6 | 3318 | gdk_threads_enter(); |
ff1f3280 | 3319 | gtk_main(); |
2839f0c6 | 3320 | gdk_threads_leave(); |
ff1f3280 SC |
3321 | return 0; |
3322 | } |