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 | |
63a130b7 | 35 | static int gfio_server_running; |
f3e8440f | 36 | static const char *gfio_graph_font; |
63a130b7 | 37 | |
6b79c80c | 38 | static void view_log(GtkWidget *w, gpointer data); |
3e47bd25 | 39 | |
f3074008 SC |
40 | #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0]))) |
41 | ||
42 | typedef void (*clickfunction)(GtkWidget *widget, gpointer data); | |
43 | ||
3e47bd25 | 44 | static void connect_clicked(GtkWidget *widget, gpointer data); |
f3074008 | 45 | static void start_job_clicked(GtkWidget *widget, gpointer data); |
b9d2f30a | 46 | static void send_clicked(GtkWidget *widget, gpointer data); |
f3074008 SC |
47 | |
48 | static struct button_spec { | |
49 | const char *buttontext; | |
50 | clickfunction f; | |
51 | const char *tooltiptext; | |
3e47bd25 | 52 | const int start_insensitive; |
f3074008 | 53 | } buttonspeclist[] = { |
3e47bd25 | 54 | #define CONNECT_BUTTON 0 |
b9d2f30a JA |
55 | #define SEND_BUTTON 1 |
56 | #define START_JOB_BUTTON 2 | |
3e47bd25 | 57 | { "Connect", connect_clicked, "Connect to host", 0 }, |
b9d2f30a JA |
58 | { "Send", send_clicked, "Send job description to host", 1 }, |
59 | { "Start Job", start_job_clicked, | |
2f99deb0 | 60 | "Start the current job on the server", 1 }, |
f3074008 SC |
61 | }; |
62 | ||
843ad237 JA |
63 | struct probe_widget { |
64 | GtkWidget *hostname; | |
65 | GtkWidget *os; | |
66 | GtkWidget *arch; | |
67 | GtkWidget *fio_ver; | |
68 | }; | |
69 | ||
3e47bd25 | 70 | struct eta_widget { |
3863d1ad | 71 | GtkWidget *names; |
807f9971 JA |
72 | GtkWidget *iotype; |
73 | GtkWidget *ioengine; | |
74 | GtkWidget *iodepth; | |
3e47bd25 JA |
75 | GtkWidget *jobs; |
76 | GtkWidget *files; | |
77 | GtkWidget *read_bw; | |
78 | GtkWidget *read_iops; | |
79 | GtkWidget *cr_bw; | |
80 | GtkWidget *cr_iops; | |
81 | GtkWidget *write_bw; | |
82 | GtkWidget *write_iops; | |
83 | GtkWidget *cw_bw; | |
84 | GtkWidget *cw_iops; | |
85 | }; | |
86 | ||
2f99deb0 JA |
87 | struct gfio_graphs { |
88 | #define DRAWING_AREA_XDIM 1000 | |
89 | #define DRAWING_AREA_YDIM 400 | |
90 | GtkWidget *drawing_area; | |
91 | int drawing_area_xdim; | |
92 | int drawing_area_ydim; | |
93 | ||
94 | struct graph *iops_graph; | |
95 | struct graph *bandwidth_graph; | |
96 | }; | |
97 | ||
98 | /* | |
99 | * Main window widgets and data | |
100 | */ | |
ff1f3280 SC |
101 | struct gui { |
102 | GtkWidget *window; | |
2f99deb0 JA |
103 | GtkWidget *vbox; |
104 | GtkWidget *topvbox; | |
105 | GtkWidget *topalign; | |
106 | GtkWidget *bottomalign; | |
107 | GtkWidget *thread_status_pb; | |
108 | GtkWidget *buttonbox; | |
109 | GtkWidget *scrolled_window; | |
110 | GtkWidget *notebook; | |
111 | GtkWidget *error_info_bar; | |
112 | GtkWidget *error_label; | |
113 | GtkListStore *log_model; | |
114 | GtkWidget *log_tree; | |
115 | GtkWidget *log_view; | |
116 | struct gfio_graphs graphs; | |
117 | struct probe_widget probe; | |
118 | struct eta_widget eta; | |
119 | pthread_t server_t; | |
120 | ||
a9eccde4 JA |
121 | pthread_t t; |
122 | int handler_running; | |
123 | ||
2f99deb0 JA |
124 | struct flist_head list; |
125 | } main_ui; | |
126 | ||
127 | /* | |
128 | * Notebook entry | |
129 | */ | |
130 | struct gui_entry { | |
131 | struct flist_head list; | |
132 | struct gui *ui; | |
133 | ||
5b7573ab | 134 | GtkWidget *vbox; |
c36f98d9 SC |
135 | GtkWidget *topvbox; |
136 | GtkWidget *topalign; | |
137 | GtkWidget *bottomalign; | |
04cc6b77 | 138 | GtkWidget *thread_status_pb; |
f3074008 SC |
139 | GtkWidget *buttonbox; |
140 | GtkWidget *button[ARRAYSIZE(buttonspeclist)]; | |
736f2dff | 141 | GtkWidget *scrolled_window; |
2f99deb0 | 142 | GtkWidget *notebook; |
0420ba6a JA |
143 | GtkWidget *error_info_bar; |
144 | GtkWidget *error_label; | |
f9d40b48 JA |
145 | GtkWidget *results_notebook; |
146 | GtkWidget *results_window; | |
9b260bdf JA |
147 | GtkListStore *log_model; |
148 | GtkWidget *log_tree; | |
4cbe7211 | 149 | GtkWidget *log_view; |
2f99deb0 | 150 | struct gfio_graphs graphs; |
843ad237 | 151 | struct probe_widget probe; |
3e47bd25 | 152 | struct eta_widget eta; |
2f99deb0 JA |
153 | GtkWidget *page_label; |
154 | gint page_num; | |
3ec62ec4 | 155 | int connected; |
0420ba6a | 156 | |
b9d2f30a | 157 | struct gfio_client *client; |
0420ba6a JA |
158 | int nr_job_files; |
159 | char **job_files; | |
2f99deb0 | 160 | }; |
ff1f3280 | 161 | |
e0681f3e | 162 | struct gfio_client { |
2f99deb0 | 163 | struct gui_entry *ge; |
b9d2f30a | 164 | struct fio_client *client; |
e0681f3e JA |
165 | GtkWidget *results_widget; |
166 | GtkWidget *disk_util_frame; | |
6b79c80c | 167 | GtkWidget *err_entry; |
dcaeb606 JA |
168 | unsigned int job_added; |
169 | struct thread_options o; | |
e0681f3e JA |
170 | }; |
171 | ||
9988ca70 JA |
172 | static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc); |
173 | static void gfio_update_thread_status_all(char *status_message, double perc); | |
c724926b | 174 | void report_error(GError *error); |
9988ca70 | 175 | |
2f99deb0 JA |
176 | static struct graph *setup_iops_graph(void) |
177 | { | |
178 | struct graph *g; | |
179 | ||
180 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
181 | graph_title(g, "IOPS"); | |
182 | graph_x_title(g, "Time (secs)"); | |
183 | graph_y_title(g, "IOs / sec"); | |
184 | graph_add_label(g, "Read IOPS"); | |
185 | graph_add_label(g, "Write IOPS"); | |
186 | graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13); | |
187 | graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0); | |
188 | line_graph_set_data_count_limit(g, 100); | |
189 | return g; | |
190 | } | |
191 | ||
192 | static struct graph *setup_bandwidth_graph(void) | |
193 | { | |
194 | struct graph *g; | |
195 | ||
196 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
197 | graph_title(g, "Bandwidth"); | |
198 | graph_x_title(g, "Time (secs)"); | |
199 | graph_y_title(g, "Kbytes / sec"); | |
200 | graph_add_label(g, "Read Bandwidth"); | |
201 | graph_add_label(g, "Write Bandwidth"); | |
202 | graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13); | |
203 | graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0); | |
204 | line_graph_set_data_count_limit(g, 100); | |
205 | return g; | |
206 | } | |
207 | ||
208 | static void setup_graphs(struct gfio_graphs *g) | |
209 | { | |
210 | g->iops_graph = setup_iops_graph(); | |
211 | g->bandwidth_graph = setup_bandwidth_graph(); | |
212 | } | |
213 | ||
214 | static void clear_ge_ui_info(struct gui_entry *ge) | |
215 | { | |
216 | gtk_label_set_text(GTK_LABEL(ge->probe.hostname), ""); | |
217 | gtk_label_set_text(GTK_LABEL(ge->probe.os), ""); | |
218 | gtk_label_set_text(GTK_LABEL(ge->probe.arch), ""); | |
219 | gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), ""); | |
3863d1ad JA |
220 | #if 0 |
221 | /* should we empty it... */ | |
2f99deb0 | 222 | gtk_entry_set_text(GTK_ENTRY(ge->eta.name), ""); |
3863d1ad | 223 | #endif |
2f99deb0 JA |
224 | gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ""); |
225 | gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), ""); | |
226 | gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), ""); | |
227 | gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), ""); | |
228 | gtk_entry_set_text(GTK_ENTRY(ge->eta.files), ""); | |
229 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), ""); | |
230 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), ""); | |
231 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), ""); | |
232 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), ""); | |
8663ea65 JA |
233 | } |
234 | ||
3863d1ad JA |
235 | static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label) |
236 | { | |
237 | GtkWidget *entry, *frame; | |
238 | ||
239 | frame = gtk_frame_new(label); | |
240 | entry = gtk_combo_box_new_text(); | |
241 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); | |
242 | gtk_container_add(GTK_CONTAINER(frame), entry); | |
243 | ||
244 | return entry; | |
245 | } | |
246 | ||
3650a3ca JA |
247 | static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label) |
248 | { | |
249 | GtkWidget *entry, *frame; | |
250 | ||
251 | frame = gtk_frame_new(label); | |
252 | entry = gtk_entry_new(); | |
1c1e4a5b | 253 | gtk_entry_set_editable(GTK_ENTRY(entry), 0); |
3650a3ca JA |
254 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); |
255 | gtk_container_add(GTK_CONTAINER(frame), entry); | |
256 | ||
257 | return entry; | |
258 | } | |
259 | ||
260 | static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) | |
261 | { | |
262 | GtkWidget *label_widget; | |
263 | GtkWidget *frame; | |
264 | ||
265 | frame = gtk_frame_new(label); | |
266 | label_widget = gtk_label_new(NULL); | |
267 | gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); | |
268 | gtk_container_add(GTK_CONTAINER(frame), label_widget); | |
269 | ||
270 | return label_widget; | |
271 | } | |
272 | ||
273 | static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval) | |
274 | { | |
275 | GtkWidget *button, *box; | |
276 | ||
277 | box = gtk_hbox_new(FALSE, 3); | |
278 | gtk_container_add(GTK_CONTAINER(hbox), box); | |
279 | ||
280 | button = gtk_spin_button_new_with_range(min, max, 1.0); | |
281 | gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); | |
282 | ||
283 | gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID); | |
284 | gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval); | |
285 | ||
286 | return button; | |
287 | } | |
288 | ||
2f99deb0 | 289 | static void gfio_set_connected(struct gui_entry *ge, int connected) |
3ec62ec4 JA |
290 | { |
291 | if (connected) { | |
2f99deb0 JA |
292 | gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1); |
293 | ge->connected = 1; | |
294 | gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect"); | |
295 | gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1); | |
3ec62ec4 | 296 | } else { |
2f99deb0 JA |
297 | ge->connected = 0; |
298 | gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect"); | |
299 | gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0); | |
300 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0); | |
301 | gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1); | |
3ec62ec4 JA |
302 | } |
303 | } | |
304 | ||
3650a3ca JA |
305 | static void label_set_int_value(GtkWidget *entry, unsigned int val) |
306 | { | |
307 | char tmp[80]; | |
308 | ||
309 | sprintf(tmp, "%u", val); | |
310 | gtk_label_set_text(GTK_LABEL(entry), tmp); | |
311 | } | |
312 | ||
313 | static void entry_set_int_value(GtkWidget *entry, unsigned int val) | |
314 | { | |
315 | char tmp[80]; | |
316 | ||
317 | sprintf(tmp, "%u", val); | |
318 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
319 | } | |
320 | ||
a269790c JA |
321 | #define ALIGN_LEFT 1 |
322 | #define ALIGN_RIGHT 2 | |
323 | #define INVISIBLE 4 | |
324 | #define UNSORTABLE 8 | |
325 | ||
326 | GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags) | |
327 | { | |
328 | GtkCellRenderer *renderer; | |
329 | GtkTreeViewColumn *col; | |
330 | double xalign = 0.0; /* left as default */ | |
331 | PangoAlignment align; | |
332 | gboolean visible; | |
333 | ||
334 | align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : | |
335 | (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : | |
336 | PANGO_ALIGN_CENTER; | |
337 | visible = !(flags & INVISIBLE); | |
338 | ||
339 | renderer = gtk_cell_renderer_text_new(); | |
340 | col = gtk_tree_view_column_new(); | |
341 | ||
342 | gtk_tree_view_column_set_title(col, title); | |
343 | if (!(flags & UNSORTABLE)) | |
344 | gtk_tree_view_column_set_sort_column_id(col, index); | |
345 | gtk_tree_view_column_set_resizable(col, TRUE); | |
346 | gtk_tree_view_column_pack_start(col, renderer, TRUE); | |
347 | gtk_tree_view_column_add_attribute(col, renderer, "text", index); | |
348 | gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL); | |
349 | switch (align) { | |
350 | case PANGO_ALIGN_LEFT: | |
351 | xalign = 0.0; | |
352 | break; | |
353 | case PANGO_ALIGN_CENTER: | |
354 | xalign = 0.5; | |
355 | break; | |
356 | case PANGO_ALIGN_RIGHT: | |
357 | xalign = 1.0; | |
358 | break; | |
359 | } | |
360 | gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); | |
361 | gtk_tree_view_column_set_visible(col, visible); | |
362 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); | |
363 | return col; | |
364 | } | |
365 | ||
9b260bdf JA |
366 | static void gfio_ui_setup_log(struct gui *ui) |
367 | { | |
368 | GtkTreeSelection *selection; | |
369 | GtkListStore *model; | |
370 | GtkWidget *tree_view; | |
371 | ||
372 | model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); | |
373 | ||
374 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
375 | gtk_widget_set_can_focus(tree_view, FALSE); | |
376 | ||
377 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); | |
378 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
661f741a JA |
379 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
380 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
9b260bdf JA |
381 | |
382 | tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE); | |
383 | tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE); | |
384 | tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE); | |
f095d563 | 385 | tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE); |
9b260bdf JA |
386 | |
387 | ui->log_model = model; | |
388 | ui->log_tree = tree_view; | |
389 | } | |
390 | ||
a269790c JA |
391 | static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, |
392 | fio_fp64_t *plist, | |
393 | unsigned int len, | |
394 | const char *base, | |
395 | unsigned int scale) | |
396 | { | |
397 | GType types[FIO_IO_U_LIST_MAX_LEN]; | |
398 | GtkWidget *tree_view; | |
399 | GtkTreeSelection *selection; | |
400 | GtkListStore *model; | |
401 | GtkTreeIter iter; | |
402 | int i; | |
403 | ||
404 | for (i = 0; i < len; i++) | |
405 | types[i] = G_TYPE_INT; | |
406 | ||
407 | model = gtk_list_store_newv(len, types); | |
408 | ||
409 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
410 | gtk_widget_set_can_focus(tree_view, FALSE); | |
411 | ||
661f741a JA |
412 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
413 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
414 | ||
a269790c JA |
415 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
416 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
417 | ||
418 | for (i = 0; i < len; i++) { | |
419 | char fbuf[8]; | |
420 | ||
421 | sprintf(fbuf, "%2.2f%%", plist[i].u.f); | |
422 | tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE); | |
423 | } | |
424 | ||
425 | gtk_list_store_append(model, &iter); | |
426 | ||
e0681f3e JA |
427 | for (i = 0; i < len; i++) { |
428 | if (scale) | |
429 | ovals[i] = (ovals[i] + 999) / 1000; | |
a269790c | 430 | gtk_list_store_set(model, &iter, i, ovals[i], -1); |
e0681f3e | 431 | } |
a269790c JA |
432 | |
433 | return tree_view; | |
434 | } | |
435 | ||
436 | static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts, | |
437 | int ddir) | |
438 | { | |
439 | unsigned int *io_u_plat = ts->io_u_plat[ddir]; | |
440 | unsigned long nr = ts->clat_stat[ddir].samples; | |
441 | fio_fp64_t *plist = ts->percentile_list; | |
442 | unsigned int *ovals, len, minv, maxv, scale_down; | |
443 | const char *base; | |
444 | GtkWidget *tree_view, *frame, *hbox; | |
445 | char tmp[64]; | |
446 | ||
447 | len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); | |
448 | if (!len) | |
449 | goto out; | |
450 | ||
451 | /* | |
452 | * We default to usecs, but if the value range is such that we | |
453 | * should scale down to msecs, do that. | |
454 | */ | |
455 | if (minv > 2000 && maxv > 99999) { | |
456 | scale_down = 1; | |
457 | base = "msec"; | |
458 | } else { | |
459 | scale_down = 0; | |
460 | base = "usec"; | |
461 | } | |
462 | ||
463 | tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); | |
464 | ||
465 | sprintf(tmp, "Completion percentiles (%s)", base); | |
466 | frame = gtk_frame_new(tmp); | |
467 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
468 | ||
469 | hbox = gtk_hbox_new(FALSE, 3); | |
470 | gtk_container_add(GTK_CONTAINER(frame), hbox); | |
471 | ||
472 | gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3); | |
473 | out: | |
474 | if (ovals) | |
475 | free(ovals); | |
476 | } | |
477 | ||
3650a3ca JA |
478 | static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, |
479 | unsigned long max, double mean, double dev) | |
480 | { | |
481 | const char *base = "(usec)"; | |
1c1e4a5b | 482 | GtkWidget *hbox, *label, *frame; |
3650a3ca JA |
483 | char *minp, *maxp; |
484 | char tmp[64]; | |
485 | ||
486 | if (!usec_to_msec(&min, &max, &mean, &dev)) | |
487 | base = "(msec)"; | |
488 | ||
489 | minp = num2str(min, 6, 1, 0); | |
490 | maxp = num2str(max, 6, 1, 0); | |
491 | ||
3650a3ca JA |
492 | sprintf(tmp, "%s %s", name, base); |
493 | frame = gtk_frame_new(tmp); | |
494 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
495 | ||
3650a3ca | 496 | hbox = gtk_hbox_new(FALSE, 3); |
1c1e4a5b | 497 | gtk_container_add(GTK_CONTAINER(frame), hbox); |
3650a3ca JA |
498 | |
499 | label = new_info_label_in_frame(hbox, "Minimum"); | |
500 | gtk_label_set_text(GTK_LABEL(label), minp); | |
501 | label = new_info_label_in_frame(hbox, "Maximum"); | |
502 | gtk_label_set_text(GTK_LABEL(label), maxp); | |
503 | label = new_info_label_in_frame(hbox, "Average"); | |
504 | sprintf(tmp, "%5.02f", mean); | |
505 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
506 | label = new_info_label_in_frame(hbox, "Standard deviation"); | |
507 | sprintf(tmp, "%5.02f", dev); | |
508 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
509 | ||
510 | free(minp); | |
511 | free(maxp); | |
512 | ||
513 | } | |
514 | ||
ca850992 JA |
515 | #define GFIO_CLAT 1 |
516 | #define GFIO_SLAT 2 | |
517 | #define GFIO_LAT 4 | |
518 | ||
3650a3ca JA |
519 | static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, |
520 | struct thread_stat *ts, int ddir) | |
521 | { | |
522 | const char *ddir_label[2] = { "Read", "Write" }; | |
0b761306 | 523 | GtkWidget *frame, *label, *box, *vbox, *main_vbox; |
e0681f3e | 524 | unsigned long min[3], max[3], runt; |
3650a3ca | 525 | unsigned long long bw, iops; |
ca850992 | 526 | unsigned int flags = 0; |
e0681f3e | 527 | double mean[3], dev[3]; |
3650a3ca JA |
528 | char *io_p, *bw_p, *iops_p; |
529 | int i2p; | |
530 | ||
531 | if (!ts->runtime[ddir]) | |
532 | return; | |
533 | ||
534 | i2p = is_power_of_2(rs->kb_base); | |
535 | runt = ts->runtime[ddir]; | |
536 | ||
537 | bw = (1000 * ts->io_bytes[ddir]) / runt; | |
538 | io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); | |
539 | bw_p = num2str(bw, 6, 1, i2p); | |
540 | ||
541 | iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; | |
542 | iops_p = num2str(iops, 6, 1, 0); | |
543 | ||
544 | box = gtk_hbox_new(FALSE, 3); | |
545 | gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); | |
546 | ||
547 | frame = gtk_frame_new(ddir_label[ddir]); | |
548 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); | |
549 | ||
0b761306 JA |
550 | main_vbox = gtk_vbox_new(FALSE, 3); |
551 | gtk_container_add(GTK_CONTAINER(frame), main_vbox); | |
3650a3ca JA |
552 | |
553 | box = gtk_hbox_new(FALSE, 3); | |
0b761306 | 554 | gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3); |
3650a3ca JA |
555 | |
556 | label = new_info_label_in_frame(box, "IO"); | |
557 | gtk_label_set_text(GTK_LABEL(label), io_p); | |
558 | label = new_info_label_in_frame(box, "Bandwidth"); | |
559 | gtk_label_set_text(GTK_LABEL(label), bw_p); | |
560 | label = new_info_label_in_frame(box, "IOPS"); | |
561 | gtk_label_set_text(GTK_LABEL(label), iops_p); | |
562 | label = new_info_label_in_frame(box, "Runtime (msec)"); | |
563 | label_set_int_value(label, ts->runtime[ddir]); | |
564 | ||
e0681f3e | 565 | if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) { |
ca850992 JA |
566 | double p_of_agg = 100.0; |
567 | const char *bw_str = "KB"; | |
568 | char tmp[32]; | |
569 | ||
570 | if (rs->agg[ddir]) { | |
e0681f3e | 571 | p_of_agg = mean[0] * 100 / (double) rs->agg[ddir]; |
ca850992 JA |
572 | if (p_of_agg > 100.0) |
573 | p_of_agg = 100.0; | |
574 | } | |
575 | ||
e0681f3e JA |
576 | if (mean[0] > 999999.9) { |
577 | min[0] /= 1000.0; | |
578 | max[0] /= 1000.0; | |
579 | mean[0] /= 1000.0; | |
580 | dev[0] /= 1000.0; | |
ca850992 JA |
581 | bw_str = "MB"; |
582 | } | |
583 | ||
0b761306 JA |
584 | sprintf(tmp, "Bandwidth (%s)", bw_str); |
585 | frame = gtk_frame_new(tmp); | |
586 | gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); | |
ca850992 | 587 | |
0b761306 JA |
588 | box = gtk_hbox_new(FALSE, 3); |
589 | gtk_container_add(GTK_CONTAINER(frame), box); | |
ca850992 | 590 | |
0b761306 | 591 | label = new_info_label_in_frame(box, "Minimum"); |
e0681f3e | 592 | label_set_int_value(label, min[0]); |
0b761306 | 593 | label = new_info_label_in_frame(box, "Maximum"); |
e0681f3e | 594 | label_set_int_value(label, max[0]); |
0b761306 | 595 | label = new_info_label_in_frame(box, "Percentage of jobs"); |
ca850992 JA |
596 | sprintf(tmp, "%3.2f%%", p_of_agg); |
597 | gtk_label_set_text(GTK_LABEL(label), tmp); | |
0b761306 | 598 | label = new_info_label_in_frame(box, "Average"); |
e0681f3e | 599 | sprintf(tmp, "%5.02f", mean[0]); |
ca850992 | 600 | gtk_label_set_text(GTK_LABEL(label), tmp); |
0b761306 | 601 | label = new_info_label_in_frame(box, "Standard deviation"); |
e0681f3e | 602 | sprintf(tmp, "%5.02f", dev[0]); |
ca850992 JA |
603 | gtk_label_set_text(GTK_LABEL(label), tmp); |
604 | } | |
605 | ||
e0681f3e | 606 | if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) |
2b089892 | 607 | flags |= GFIO_SLAT; |
e0681f3e | 608 | if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1])) |
2b089892 | 609 | flags |= GFIO_CLAT; |
e0681f3e | 610 | if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2])) |
2b089892 JA |
611 | flags |= GFIO_LAT; |
612 | ||
613 | if (flags) { | |
614 | frame = gtk_frame_new("Latency"); | |
615 | gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); | |
616 | ||
617 | vbox = gtk_vbox_new(FALSE, 3); | |
618 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
619 | ||
620 | if (flags & GFIO_SLAT) | |
e0681f3e | 621 | gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]); |
2b089892 | 622 | if (flags & GFIO_CLAT) |
e0681f3e | 623 | gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]); |
2b089892 | 624 | if (flags & GFIO_LAT) |
e0681f3e | 625 | gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]); |
2b089892 JA |
626 | } |
627 | ||
628 | if (ts->clat_percentiles) | |
629 | gfio_show_clat_percentiles(main_vbox, ts, ddir); | |
630 | ||
631 | ||
3650a3ca JA |
632 | free(io_p); |
633 | free(bw_p); | |
634 | free(iops_p); | |
635 | } | |
636 | ||
e5bd1347 JA |
637 | static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num, |
638 | const char **labels) | |
639 | { | |
640 | GtkWidget *tree_view; | |
641 | GtkTreeSelection *selection; | |
642 | GtkListStore *model; | |
643 | GtkTreeIter iter; | |
644 | GType *types; | |
645 | int i, skipped; | |
646 | ||
647 | /* | |
648 | * Check if all are empty, in which case don't bother | |
649 | */ | |
650 | for (i = 0, skipped = 0; i < num; i++) | |
651 | if (lat[i] <= 0.0) | |
652 | skipped++; | |
653 | ||
654 | if (skipped == num) | |
655 | return NULL; | |
656 | ||
657 | types = malloc(num * sizeof(GType)); | |
658 | ||
659 | for (i = 0; i < num; i++) | |
660 | types[i] = G_TYPE_STRING; | |
661 | ||
662 | model = gtk_list_store_newv(num, types); | |
663 | free(types); | |
664 | types = NULL; | |
665 | ||
666 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
667 | gtk_widget_set_can_focus(tree_view, FALSE); | |
668 | ||
661f741a JA |
669 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
670 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
671 | ||
e5bd1347 JA |
672 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
673 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
674 | ||
675 | for (i = 0; i < num; i++) | |
676 | tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); | |
677 | ||
678 | gtk_list_store_append(model, &iter); | |
679 | ||
680 | for (i = 0; i < num; i++) { | |
681 | char fbuf[32]; | |
682 | ||
683 | if (lat[i] <= 0.0) | |
684 | sprintf(fbuf, "0.00"); | |
685 | else | |
686 | sprintf(fbuf, "%3.2f%%", lat[i]); | |
687 | ||
688 | gtk_list_store_set(model, &iter, i, fbuf, -1); | |
689 | } | |
690 | ||
691 | return tree_view; | |
692 | } | |
693 | ||
694 | static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts) | |
695 | { | |
696 | GtkWidget *box, *frame, *tree_view; | |
697 | double io_u_lat_u[FIO_IO_U_LAT_U_NR]; | |
698 | double io_u_lat_m[FIO_IO_U_LAT_M_NR]; | |
699 | const char *uranges[] = { "2", "4", "10", "20", "50", "100", | |
700 | "250", "500", "750", "1000", }; | |
701 | const char *mranges[] = { "2", "4", "10", "20", "50", "100", | |
702 | "250", "500", "750", "1000", "2000", | |
703 | ">= 2000", }; | |
704 | ||
705 | stat_calc_lat_u(ts, io_u_lat_u); | |
706 | stat_calc_lat_m(ts, io_u_lat_m); | |
707 | ||
708 | tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges); | |
709 | if (tree_view) { | |
710 | frame = gtk_frame_new("Latency buckets (usec)"); | |
711 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
712 | ||
713 | box = gtk_hbox_new(FALSE, 3); | |
714 | gtk_container_add(GTK_CONTAINER(frame), box); | |
715 | gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); | |
716 | } | |
717 | ||
718 | tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges); | |
719 | if (tree_view) { | |
720 | frame = gtk_frame_new("Latency buckets (msec)"); | |
721 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
722 | ||
723 | box = gtk_hbox_new(FALSE, 3); | |
724 | gtk_container_add(GTK_CONTAINER(frame), box); | |
725 | gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); | |
726 | } | |
727 | } | |
728 | ||
2e33101f JA |
729 | static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts) |
730 | { | |
731 | GtkWidget *box, *frame, *entry; | |
732 | double usr_cpu, sys_cpu; | |
733 | unsigned long runtime; | |
734 | char tmp[32]; | |
735 | ||
736 | runtime = ts->total_run_time; | |
737 | if (runtime) { | |
738 | double runt = (double) runtime; | |
739 | ||
740 | usr_cpu = (double) ts->usr_time * 100 / runt; | |
741 | sys_cpu = (double) ts->sys_time * 100 / runt; | |
742 | } else { | |
743 | usr_cpu = 0; | |
744 | sys_cpu = 0; | |
745 | } | |
746 | ||
747 | frame = gtk_frame_new("OS resources"); | |
748 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
749 | ||
750 | box = gtk_hbox_new(FALSE, 3); | |
751 | gtk_container_add(GTK_CONTAINER(frame), box); | |
752 | ||
753 | entry = new_info_entry_in_frame(box, "User CPU"); | |
754 | sprintf(tmp, "%3.2f%%", usr_cpu); | |
755 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
756 | entry = new_info_entry_in_frame(box, "System CPU"); | |
757 | sprintf(tmp, "%3.2f%%", sys_cpu); | |
758 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
759 | entry = new_info_entry_in_frame(box, "Context switches"); | |
760 | entry_set_int_value(entry, ts->ctx); | |
761 | entry = new_info_entry_in_frame(box, "Major faults"); | |
762 | entry_set_int_value(entry, ts->majf); | |
763 | entry = new_info_entry_in_frame(box, "Minor faults"); | |
764 | entry_set_int_value(entry, ts->minf); | |
765 | } | |
19998dbc JA |
766 | static void gfio_add_sc_depths_tree(GtkListStore *model, |
767 | struct thread_stat *ts, unsigned int len, | |
768 | int submit) | |
2e33101f JA |
769 | { |
770 | double io_u_dist[FIO_IO_U_MAP_NR]; | |
2e33101f | 771 | GtkTreeIter iter; |
19998dbc JA |
772 | /* Bits 0, and 3-8 */ |
773 | const int add_mask = 0x1f9; | |
774 | int i, j; | |
2e33101f | 775 | |
19998dbc JA |
776 | if (submit) |
777 | stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist); | |
778 | else | |
779 | stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist); | |
2e33101f | 780 | |
19998dbc | 781 | gtk_list_store_append(model, &iter); |
2e33101f | 782 | |
19998dbc | 783 | gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1); |
2e33101f | 784 | |
19998dbc JA |
785 | for (i = 1, j = 0; i < len; i++) { |
786 | char fbuf[32]; | |
2e33101f | 787 | |
19998dbc JA |
788 | if (!(add_mask & (1UL << (i - 1)))) |
789 | sprintf(fbuf, "0.0%%"); | |
790 | else { | |
791 | sprintf(fbuf, "%3.1f%%", io_u_dist[j]); | |
792 | j++; | |
793 | } | |
2e33101f | 794 | |
19998dbc JA |
795 | gtk_list_store_set(model, &iter, i, fbuf, -1); |
796 | } | |
2e33101f | 797 | |
19998dbc | 798 | } |
2e33101f | 799 | |
19998dbc JA |
800 | static void gfio_add_total_depths_tree(GtkListStore *model, |
801 | struct thread_stat *ts, unsigned int len) | |
802 | { | |
803 | double io_u_dist[FIO_IO_U_MAP_NR]; | |
804 | GtkTreeIter iter; | |
805 | /* Bits 1-6, and 8 */ | |
806 | const int add_mask = 0x17e; | |
807 | int i, j; | |
808 | ||
809 | stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); | |
2e33101f JA |
810 | |
811 | gtk_list_store_append(model, &iter); | |
812 | ||
19998dbc JA |
813 | gtk_list_store_set(model, &iter, 0, "Total", -1); |
814 | ||
815 | for (i = 1, j = 0; i < len; i++) { | |
2e33101f JA |
816 | char fbuf[32]; |
817 | ||
19998dbc JA |
818 | if (!(add_mask & (1UL << (i - 1)))) |
819 | sprintf(fbuf, "0.0%%"); | |
820 | else { | |
821 | sprintf(fbuf, "%3.1f%%", io_u_dist[j]); | |
822 | j++; | |
2e33101f JA |
823 | } |
824 | ||
2e33101f JA |
825 | gtk_list_store_set(model, &iter, i, fbuf, -1); |
826 | } | |
827 | ||
19998dbc | 828 | } |
2e33101f | 829 | |
19998dbc JA |
830 | static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) |
831 | { | |
832 | GtkWidget *frame, *box, *tree_view; | |
833 | GtkTreeSelection *selection; | |
834 | GtkListStore *model; | |
835 | GType types[FIO_IO_U_MAP_NR + 1]; | |
836 | int i; | |
837 | #define NR_LABELS 10 | |
838 | const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" }; | |
2e33101f | 839 | |
19998dbc JA |
840 | frame = gtk_frame_new("IO depths"); |
841 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
2e33101f | 842 | |
19998dbc JA |
843 | box = gtk_hbox_new(FALSE, 3); |
844 | gtk_container_add(GTK_CONTAINER(frame), box); | |
2e33101f | 845 | |
19998dbc JA |
846 | for (i = 0; i < NR_LABELS; i++) |
847 | types[i] = G_TYPE_STRING; | |
2e33101f | 848 | |
19998dbc | 849 | model = gtk_list_store_newv(NR_LABELS, types); |
2e33101f | 850 | |
19998dbc JA |
851 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); |
852 | gtk_widget_set_can_focus(tree_view, FALSE); | |
2e33101f | 853 | |
661f741a JA |
854 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
855 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
856 | ||
19998dbc JA |
857 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); |
858 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
859 | ||
860 | for (i = 0; i < NR_LABELS; i++) | |
861 | tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); | |
862 | ||
863 | gfio_add_total_depths_tree(model, ts, NR_LABELS); | |
864 | gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1); | |
865 | gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0); | |
2e33101f JA |
866 | |
867 | gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); | |
868 | } | |
869 | ||
f9d40b48 JA |
870 | static gboolean results_window_delete(GtkWidget *w, gpointer data) |
871 | { | |
2f99deb0 | 872 | struct gui_entry *ge = (struct gui_entry *) data; |
f9d40b48 JA |
873 | |
874 | gtk_widget_destroy(w); | |
2f99deb0 JA |
875 | ge->results_window = NULL; |
876 | ge->results_notebook = NULL; | |
f9d40b48 JA |
877 | return TRUE; |
878 | } | |
879 | ||
2f99deb0 | 880 | static GtkWidget *get_results_window(struct gui_entry *ge) |
f9d40b48 JA |
881 | { |
882 | GtkWidget *win, *notebook; | |
883 | ||
2f99deb0 JA |
884 | if (ge->results_window) |
885 | return ge->results_notebook; | |
f9d40b48 JA |
886 | |
887 | win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
888 | gtk_window_set_title(GTK_WINDOW(win), "Results"); | |
b01329d0 | 889 | gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768); |
2f99deb0 JA |
890 | g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge); |
891 | g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge); | |
f9d40b48 JA |
892 | |
893 | notebook = gtk_notebook_new(); | |
0aa928c4 JA |
894 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1); |
895 | gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); | |
f9d40b48 JA |
896 | gtk_container_add(GTK_CONTAINER(win), notebook); |
897 | ||
2f99deb0 JA |
898 | ge->results_window = win; |
899 | ge->results_notebook = notebook; | |
900 | return ge->results_notebook; | |
f9d40b48 JA |
901 | } |
902 | ||
3650a3ca JA |
903 | static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, |
904 | struct group_run_stats *rs) | |
905 | { | |
b01329d0 | 906 | GtkWidget *res_win, *box, *vbox, *entry, *scroll; |
e0681f3e | 907 | struct gfio_client *gc = client->client_data; |
3650a3ca JA |
908 | |
909 | gdk_threads_enter(); | |
910 | ||
2f99deb0 | 911 | res_win = get_results_window(gc->ge); |
3650a3ca | 912 | |
b01329d0 JA |
913 | scroll = gtk_scrolled_window_new(NULL, NULL); |
914 | gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); | |
915 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
916 | ||
3650a3ca | 917 | vbox = gtk_vbox_new(FALSE, 3); |
3650a3ca | 918 | |
b01329d0 JA |
919 | box = gtk_hbox_new(FALSE, 0); |
920 | gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5); | |
921 | ||
922 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); | |
3650a3ca | 923 | |
b01329d0 | 924 | gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name)); |
f9d40b48 | 925 | |
e0681f3e JA |
926 | gc->results_widget = vbox; |
927 | ||
3650a3ca JA |
928 | entry = new_info_entry_in_frame(box, "Name"); |
929 | gtk_entry_set_text(GTK_ENTRY(entry), ts->name); | |
930 | if (strlen(ts->description)) { | |
931 | entry = new_info_entry_in_frame(box, "Description"); | |
932 | gtk_entry_set_text(GTK_ENTRY(entry), ts->description); | |
933 | } | |
934 | entry = new_info_entry_in_frame(box, "Group ID"); | |
935 | entry_set_int_value(entry, ts->groupid); | |
936 | entry = new_info_entry_in_frame(box, "Jobs"); | |
937 | entry_set_int_value(entry, ts->members); | |
6b79c80c | 938 | gc->err_entry = entry = new_info_entry_in_frame(box, "Error"); |
3650a3ca JA |
939 | entry_set_int_value(entry, ts->error); |
940 | entry = new_info_entry_in_frame(box, "PID"); | |
941 | entry_set_int_value(entry, ts->pid); | |
942 | ||
943 | if (ts->io_bytes[DDIR_READ]) | |
944 | gfio_show_ddir_status(vbox, rs, ts, DDIR_READ); | |
945 | if (ts->io_bytes[DDIR_WRITE]) | |
946 | gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE); | |
947 | ||
e5bd1347 | 948 | gfio_show_latency_buckets(vbox, ts); |
2e33101f JA |
949 | gfio_show_cpu_usage(vbox, ts); |
950 | gfio_show_io_depths(vbox, ts); | |
e5bd1347 | 951 | |
2f99deb0 | 952 | gtk_widget_show_all(gc->ge->results_window); |
3650a3ca JA |
953 | gdk_threads_leave(); |
954 | } | |
955 | ||
084d1c6f | 956 | static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) |
a1820207 | 957 | { |
9b260bdf | 958 | struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload; |
2f99deb0 | 959 | struct gui *ui = &main_ui; |
9b260bdf JA |
960 | GtkTreeIter iter; |
961 | struct tm *tm; | |
962 | time_t sec; | |
963 | char tmp[64], timebuf[80]; | |
964 | ||
965 | sec = p->log_sec; | |
966 | tm = localtime(&sec); | |
967 | strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm); | |
968 | sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000); | |
736f2dff | 969 | |
736f2dff | 970 | gdk_threads_enter(); |
9b260bdf | 971 | |
2f99deb0 JA |
972 | gtk_list_store_append(ui->log_model, &iter); |
973 | gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1); | |
974 | gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1); | |
975 | gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1); | |
976 | gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1); | |
9b260bdf | 977 | |
6b79c80c | 978 | if (p->level == FIO_LOG_ERR) |
2f99deb0 | 979 | view_log(NULL, (gpointer) ui); |
6b79c80c | 980 | |
736f2dff | 981 | gdk_threads_leave(); |
a1820207 SC |
982 | } |
983 | ||
984 | static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) | |
985 | { | |
e0681f3e JA |
986 | struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload; |
987 | struct gfio_client *gc = client->client_data; | |
988 | GtkWidget *box, *frame, *entry, *vbox; | |
604cfe30 JA |
989 | double util; |
990 | char tmp[16]; | |
e0681f3e | 991 | |
0050e5f2 | 992 | gdk_threads_enter(); |
e0681f3e | 993 | |
45dcb2eb | 994 | if (!gc->results_widget) |
e0681f3e | 995 | goto out; |
e0681f3e JA |
996 | |
997 | if (!gc->disk_util_frame) { | |
998 | gc->disk_util_frame = gtk_frame_new("Disk utilization"); | |
999 | gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5); | |
1000 | } | |
1001 | ||
1002 | vbox = gtk_vbox_new(FALSE, 3); | |
1003 | gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox); | |
1004 | ||
1005 | frame = gtk_frame_new((char *) p->dus.name); | |
1006 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); | |
1007 | ||
1008 | box = gtk_vbox_new(FALSE, 3); | |
1009 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1010 | ||
1011 | frame = gtk_frame_new("Read"); | |
1012 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1013 | vbox = gtk_hbox_new(TRUE, 3); | |
1014 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1015 | entry = new_info_entry_in_frame(vbox, "IOs"); | |
1016 | entry_set_int_value(entry, p->dus.ios[0]); | |
1017 | entry = new_info_entry_in_frame(vbox, "Merges"); | |
1018 | entry_set_int_value(entry, p->dus.merges[0]); | |
1019 | entry = new_info_entry_in_frame(vbox, "Sectors"); | |
1020 | entry_set_int_value(entry, p->dus.sectors[0]); | |
1021 | entry = new_info_entry_in_frame(vbox, "Ticks"); | |
1022 | entry_set_int_value(entry, p->dus.ticks[0]); | |
1023 | ||
1024 | frame = gtk_frame_new("Write"); | |
1025 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1026 | vbox = gtk_hbox_new(TRUE, 3); | |
1027 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1028 | entry = new_info_entry_in_frame(vbox, "IOs"); | |
1029 | entry_set_int_value(entry, p->dus.ios[1]); | |
1030 | entry = new_info_entry_in_frame(vbox, "Merges"); | |
1031 | entry_set_int_value(entry, p->dus.merges[1]); | |
1032 | entry = new_info_entry_in_frame(vbox, "Sectors"); | |
1033 | entry_set_int_value(entry, p->dus.sectors[1]); | |
1034 | entry = new_info_entry_in_frame(vbox, "Ticks"); | |
1035 | entry_set_int_value(entry, p->dus.ticks[1]); | |
1036 | ||
1037 | frame = gtk_frame_new("Shared"); | |
1038 | gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); | |
1039 | vbox = gtk_hbox_new(TRUE, 3); | |
1040 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1041 | entry = new_info_entry_in_frame(vbox, "IO ticks"); | |
1042 | entry_set_int_value(entry, p->dus.io_ticks); | |
1043 | entry = new_info_entry_in_frame(vbox, "Time in queue"); | |
1044 | entry_set_int_value(entry, p->dus.time_in_queue); | |
1045 | ||
604cfe30 JA |
1046 | util = 0.0; |
1047 | if (p->dus.msec) | |
1048 | util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec; | |
1049 | if (util > 100.0) | |
1050 | util = 100.0; | |
1051 | ||
1052 | sprintf(tmp, "%3.2f%%", util); | |
1053 | entry = new_info_entry_in_frame(vbox, "Disk utilization"); | |
1054 | gtk_entry_set_text(GTK_ENTRY(entry), tmp); | |
1055 | ||
e0681f3e JA |
1056 | gtk_widget_show_all(gc->results_widget); |
1057 | out: | |
0050e5f2 | 1058 | gdk_threads_leave(); |
a1820207 SC |
1059 | } |
1060 | ||
3650a3ca JA |
1061 | extern int sum_stat_clients; |
1062 | extern struct thread_stat client_ts; | |
1063 | extern struct group_run_stats client_gs; | |
1064 | ||
1065 | static int sum_stat_nr; | |
1066 | ||
89e5fad9 JA |
1067 | static void gfio_thread_status_op(struct fio_client *client, |
1068 | struct fio_net_cmd *cmd) | |
a1820207 | 1069 | { |
3650a3ca JA |
1070 | struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; |
1071 | ||
1072 | gfio_display_ts(client, &p->ts, &p->rs); | |
1073 | ||
1074 | if (sum_stat_clients == 1) | |
1075 | return; | |
1076 | ||
1077 | sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); | |
1078 | sum_group_stats(&client_gs, &p->rs); | |
1079 | ||
1080 | client_ts.members++; | |
1081 | client_ts.groupid = p->ts.groupid; | |
1082 | ||
1083 | if (++sum_stat_nr == sum_stat_clients) { | |
1084 | strcpy(client_ts.name, "All clients"); | |
1085 | gfio_display_ts(client, &client_ts, &client_gs); | |
1086 | } | |
a1820207 SC |
1087 | } |
1088 | ||
89e5fad9 JA |
1089 | static void gfio_group_stats_op(struct fio_client *client, |
1090 | struct fio_net_cmd *cmd) | |
a1820207 | 1091 | { |
98ceabd6 | 1092 | /* We're ignoring group stats for now */ |
a1820207 SC |
1093 | } |
1094 | ||
2f99deb0 JA |
1095 | static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event, |
1096 | gpointer data) | |
3ea48b88 | 1097 | { |
2f99deb0 JA |
1098 | struct gfio_graphs *g = data; |
1099 | ||
1100 | g->drawing_area_xdim = w->allocation.width; | |
1101 | g->drawing_area_ydim = w->allocation.height; | |
3ea48b88 SC |
1102 | return TRUE; |
1103 | } | |
1104 | ||
2fd3bb0e JA |
1105 | static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) |
1106 | { | |
2f99deb0 | 1107 | struct gfio_graphs *g = p; |
2fd3bb0e JA |
1108 | cairo_t *cr; |
1109 | ||
2f99deb0 JA |
1110 | graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0, |
1111 | g->drawing_area_ydim); | |
1112 | graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0, | |
1113 | g->drawing_area_ydim); | |
2fd3bb0e JA |
1114 | cr = gdk_cairo_create(w->window); |
1115 | ||
1116 | cairo_set_source_rgb(cr, 0, 0, 0); | |
1117 | ||
1118 | cairo_save(cr); | |
1119 | cairo_translate(cr, 0, 0); | |
2f99deb0 | 1120 | line_graph_draw(g->bandwidth_graph, cr); |
2fd3bb0e JA |
1121 | cairo_stroke(cr); |
1122 | cairo_restore(cr); | |
1123 | ||
1124 | cairo_save(cr); | |
2f99deb0 JA |
1125 | cairo_translate(cr, g->drawing_area_xdim / 2.0, 0); |
1126 | line_graph_draw(g->iops_graph, cr); | |
2fd3bb0e JA |
1127 | cairo_stroke(cr); |
1128 | cairo_restore(cr); | |
1129 | cairo_destroy(cr); | |
1130 | ||
1131 | return FALSE; | |
1132 | } | |
1133 | ||
2f99deb0 JA |
1134 | /* |
1135 | * Client specific ETA | |
1136 | */ | |
1137 | static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je) | |
3e47bd25 | 1138 | { |
2f99deb0 JA |
1139 | struct gfio_client *gc = client->client_data; |
1140 | struct gui_entry *ge = gc->ge; | |
3e47bd25 JA |
1141 | static int eta_good; |
1142 | char eta_str[128]; | |
1143 | char output[256]; | |
1144 | char tmp[32]; | |
1145 | double perc = 0.0; | |
1146 | int i2p = 0; | |
1147 | ||
0050e5f2 JA |
1148 | gdk_threads_enter(); |
1149 | ||
3e47bd25 JA |
1150 | eta_str[0] = '\0'; |
1151 | output[0] = '\0'; | |
1152 | ||
1153 | if (je->eta_sec != INT_MAX && je->elapsed_sec) { | |
1154 | perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); | |
1155 | eta_to_str(eta_str, je->eta_sec); | |
1156 | } | |
1157 | ||
1158 | sprintf(tmp, "%u", je->nr_running); | |
2f99deb0 | 1159 | gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp); |
3e47bd25 | 1160 | sprintf(tmp, "%u", je->files_open); |
2f99deb0 | 1161 | gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp); |
3e47bd25 JA |
1162 | |
1163 | #if 0 | |
1164 | if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { | |
1165 | if (je->m_rate || je->t_rate) { | |
1166 | char *tr, *mr; | |
1167 | ||
1168 | mr = num2str(je->m_rate, 4, 0, i2p); | |
1169 | tr = num2str(je->t_rate, 4, 0, i2p); | |
2f99deb0 | 1170 | gtk_entry_set_text(GTK_ENTRY(ge->eta); |
3e47bd25 JA |
1171 | p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); |
1172 | free(tr); | |
1173 | free(mr); | |
1174 | } else if (je->m_iops || je->t_iops) | |
1175 | p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); | |
ebbd89cc | 1176 | |
2f99deb0 JA |
1177 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---"); |
1178 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---"); | |
1179 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---"); | |
1180 | gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---"); | |
3e47bd25 JA |
1181 | #endif |
1182 | ||
1183 | if (je->eta_sec != INT_MAX && je->nr_running) { | |
1184 | char *iops_str[2]; | |
1185 | char *rate_str[2]; | |
1186 | ||
1187 | if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) | |
1188 | strcpy(output, "-.-% done"); | |
1189 | else { | |
1190 | eta_good = 1; | |
1191 | perc *= 100.0; | |
1192 | sprintf(output, "%3.1f%% done", perc); | |
1193 | } | |
1194 | ||
1195 | rate_str[0] = num2str(je->rate[0], 5, 10, i2p); | |
1196 | rate_str[1] = num2str(je->rate[1], 5, 10, i2p); | |
1197 | ||
1198 | iops_str[0] = num2str(je->iops[0], 4, 1, 0); | |
1199 | iops_str[1] = num2str(je->iops[1], 4, 1, 0); | |
1200 | ||
2f99deb0 JA |
1201 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]); |
1202 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]); | |
1203 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]); | |
1204 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]); | |
3e47bd25 | 1205 | |
2f99deb0 JA |
1206 | graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]); |
1207 | graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]); | |
1208 | graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]); | |
1209 | graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]); | |
1210 | ||
1211 | free(rate_str[0]); | |
1212 | free(rate_str[1]); | |
1213 | free(iops_str[0]); | |
1214 | free(iops_str[1]); | |
1215 | } | |
1216 | ||
1217 | if (eta_str[0]) { | |
1218 | char *dst = output + strlen(output); | |
1219 | ||
1220 | sprintf(dst, " - %s", eta_str); | |
1221 | } | |
1222 | ||
9988ca70 | 1223 | gfio_update_thread_status(ge, output, perc); |
2f99deb0 JA |
1224 | gdk_threads_leave(); |
1225 | } | |
1226 | ||
1227 | /* | |
1228 | * Update ETA in main window for all clients | |
1229 | */ | |
1230 | static void gfio_update_all_eta(struct jobs_eta *je) | |
1231 | { | |
1232 | struct gui *ui = &main_ui; | |
1233 | static int eta_good; | |
1234 | char eta_str[128]; | |
1235 | char output[256]; | |
1236 | double perc = 0.0; | |
1237 | int i2p = 0; | |
1238 | ||
1239 | gdk_threads_enter(); | |
1240 | ||
1241 | eta_str[0] = '\0'; | |
1242 | output[0] = '\0'; | |
1243 | ||
1244 | if (je->eta_sec != INT_MAX && je->elapsed_sec) { | |
1245 | perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); | |
1246 | eta_to_str(eta_str, je->eta_sec); | |
1247 | } | |
1248 | ||
1249 | #if 0 | |
1250 | if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { | |
1251 | if (je->m_rate || je->t_rate) { | |
1252 | char *tr, *mr; | |
1253 | ||
1254 | mr = num2str(je->m_rate, 4, 0, i2p); | |
1255 | tr = num2str(je->t_rate, 4, 0, i2p); | |
1256 | gtk_entry_set_text(GTK_ENTRY(ui->eta); | |
1257 | p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); | |
1258 | free(tr); | |
1259 | free(mr); | |
1260 | } else if (je->m_iops || je->t_iops) | |
1261 | p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); | |
1262 | ||
1263 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---"); | |
1264 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---"); | |
1265 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---"); | |
1266 | gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---"); | |
1267 | #endif | |
1268 | ||
3863d1ad JA |
1269 | entry_set_int_value(ui->eta.jobs, je->nr_running); |
1270 | ||
2f99deb0 JA |
1271 | if (je->eta_sec != INT_MAX && je->nr_running) { |
1272 | char *iops_str[2]; | |
1273 | char *rate_str[2]; | |
1274 | ||
1275 | if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) | |
1276 | strcpy(output, "-.-% done"); | |
1277 | else { | |
1278 | eta_good = 1; | |
1279 | perc *= 100.0; | |
1280 | sprintf(output, "%3.1f%% done", perc); | |
1281 | } | |
1282 | ||
1283 | rate_str[0] = num2str(je->rate[0], 5, 10, i2p); | |
1284 | rate_str[1] = num2str(je->rate[1], 5, 10, i2p); | |
1285 | ||
1286 | iops_str[0] = num2str(je->iops[0], 4, 1, 0); | |
1287 | iops_str[1] = num2str(je->iops[1], 4, 1, 0); | |
1288 | ||
1289 | gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]); | |
1290 | gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]); | |
1291 | gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]); | |
1292 | gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]); | |
1293 | ||
1294 | graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]); | |
1295 | graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]); | |
1296 | graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]); | |
1297 | graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]); | |
2fd3bb0e | 1298 | |
3e47bd25 JA |
1299 | free(rate_str[0]); |
1300 | free(rate_str[1]); | |
1301 | free(iops_str[0]); | |
1302 | free(iops_str[1]); | |
1303 | } | |
1304 | ||
1305 | if (eta_str[0]) { | |
1306 | char *dst = output + strlen(output); | |
1307 | ||
1308 | sprintf(dst, " - %s", eta_str); | |
1309 | } | |
1310 | ||
9988ca70 | 1311 | gfio_update_thread_status_all(output, perc); |
0050e5f2 | 1312 | gdk_threads_leave(); |
3e47bd25 JA |
1313 | } |
1314 | ||
a1820207 SC |
1315 | static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) |
1316 | { | |
843ad237 | 1317 | struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; |
88f6e7ad | 1318 | struct gfio_client *gc = client->client_data; |
2f99deb0 | 1319 | struct gui_entry *ge = gc->ge; |
843ad237 JA |
1320 | const char *os, *arch; |
1321 | char buf[64]; | |
1322 | ||
1323 | os = fio_get_os_string(probe->os); | |
1324 | if (!os) | |
1325 | os = "unknown"; | |
1326 | ||
1327 | arch = fio_get_arch_string(probe->arch); | |
1328 | if (!arch) | |
1329 | os = "unknown"; | |
1330 | ||
1331 | if (!client->name) | |
1332 | client->name = strdup((char *) probe->hostname); | |
1333 | ||
0050e5f2 JA |
1334 | gdk_threads_enter(); |
1335 | ||
2f99deb0 JA |
1336 | gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname); |
1337 | gtk_label_set_text(GTK_LABEL(ge->probe.os), os); | |
1338 | gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch); | |
843ad237 | 1339 | sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); |
2f99deb0 | 1340 | gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf); |
88f6e7ad | 1341 | |
2f99deb0 | 1342 | gfio_set_connected(ge, 1); |
0050e5f2 JA |
1343 | |
1344 | gdk_threads_leave(); | |
a1820207 SC |
1345 | } |
1346 | ||
9988ca70 JA |
1347 | static void gfio_update_thread_status(struct gui_entry *ge, |
1348 | char *status_message, double perc) | |
1349 | { | |
1350 | static char message[100]; | |
1351 | const char *m = message; | |
1352 | ||
1353 | strncpy(message, status_message, sizeof(message) - 1); | |
1354 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m); | |
1355 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0); | |
1356 | gtk_widget_queue_draw(main_ui.window); | |
1357 | } | |
1358 | ||
1359 | static void gfio_update_thread_status_all(char *status_message, double perc) | |
5b7573ab | 1360 | { |
2f99deb0 | 1361 | struct gui *ui = &main_ui; |
5b7573ab SC |
1362 | static char message[100]; |
1363 | const char *m = message; | |
1364 | ||
1365 | strncpy(message, status_message, sizeof(message) - 1); | |
2f99deb0 JA |
1366 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m); |
1367 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0); | |
1368 | gtk_widget_queue_draw(ui->window); | |
5b7573ab SC |
1369 | } |
1370 | ||
3ec62ec4 JA |
1371 | static void gfio_quit_op(struct fio_client *client) |
1372 | { | |
e0681f3e | 1373 | struct gfio_client *gc = client->client_data; |
3ec62ec4 | 1374 | |
0050e5f2 | 1375 | gdk_threads_enter(); |
2f99deb0 | 1376 | gfio_set_connected(gc->ge, 0); |
0050e5f2 | 1377 | gdk_threads_leave(); |
3ec62ec4 JA |
1378 | } |
1379 | ||
807f9971 JA |
1380 | static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) |
1381 | { | |
1382 | struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; | |
e0681f3e | 1383 | struct gfio_client *gc = client->client_data; |
dcaeb606 | 1384 | struct thread_options *o = &gc->o; |
2f99deb0 | 1385 | struct gui_entry *ge = gc->ge; |
807f9971 | 1386 | char tmp[8]; |
807f9971 | 1387 | |
dcaeb606 | 1388 | convert_thread_options_to_cpu(o, &p->top); |
807f9971 | 1389 | |
0050e5f2 JA |
1390 | gdk_threads_enter(); |
1391 | ||
2f99deb0 JA |
1392 | gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name); |
1393 | ||
3863d1ad JA |
1394 | gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name); |
1395 | gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0); | |
1396 | ||
2f99deb0 JA |
1397 | gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir)); |
1398 | gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine); | |
807f9971 | 1399 | |
dcaeb606 | 1400 | sprintf(tmp, "%u", o->iodepth); |
2f99deb0 | 1401 | gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp); |
0050e5f2 | 1402 | |
dcaeb606 JA |
1403 | gc->job_added++; |
1404 | ||
0050e5f2 | 1405 | gdk_threads_leave(); |
807f9971 JA |
1406 | } |
1407 | ||
ed727a46 JA |
1408 | static void gfio_client_timed_out(struct fio_client *client) |
1409 | { | |
e0681f3e | 1410 | struct gfio_client *gc = client->client_data; |
ed727a46 JA |
1411 | GtkWidget *dialog, *label, *content; |
1412 | char buf[256]; | |
1413 | ||
1414 | gdk_threads_enter(); | |
1415 | ||
2f99deb0 JA |
1416 | gfio_set_connected(gc->ge, 0); |
1417 | clear_ge_ui_info(gc->ge); | |
ed727a46 JA |
1418 | |
1419 | sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); | |
1420 | ||
1421 | dialog = gtk_dialog_new_with_buttons("Timed out!", | |
2f99deb0 | 1422 | GTK_WINDOW(main_ui.window), |
ed727a46 JA |
1423 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, |
1424 | GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); | |
1425 | ||
f129909e JA |
1426 | /* gtk_dialog_get_content_area() is 2.14 and newer */ |
1427 | content = GTK_DIALOG(dialog)->vbox; | |
1428 | ||
ed727a46 JA |
1429 | label = gtk_label_new((const gchar *) buf); |
1430 | gtk_container_add(GTK_CONTAINER(content), label); | |
1431 | gtk_widget_show_all(dialog); | |
1432 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); | |
1433 | ||
1434 | gtk_dialog_run(GTK_DIALOG(dialog)); | |
1435 | gtk_widget_destroy(dialog); | |
1436 | ||
1437 | gdk_threads_leave(); | |
1438 | } | |
1439 | ||
6b79c80c JA |
1440 | static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd) |
1441 | { | |
1442 | struct gfio_client *gc = client->client_data; | |
1443 | ||
1444 | gdk_threads_enter(); | |
1445 | ||
2f99deb0 | 1446 | gfio_set_connected(gc->ge, 0); |
6b79c80c JA |
1447 | |
1448 | if (gc->err_entry) | |
1449 | entry_set_int_value(gc->err_entry, client->error); | |
1450 | ||
1451 | gdk_threads_leave(); | |
1452 | } | |
1453 | ||
a1820207 | 1454 | struct client_ops gfio_client_ops = { |
0420ba6a JA |
1455 | .text_op = gfio_text_op, |
1456 | .disk_util = gfio_disk_util_op, | |
1457 | .thread_status = gfio_thread_status_op, | |
1458 | .group_stats = gfio_group_stats_op, | |
2f99deb0 JA |
1459 | .jobs_eta = gfio_update_client_eta, |
1460 | .eta = gfio_update_all_eta, | |
0420ba6a | 1461 | .probe = gfio_probe_op, |
3ec62ec4 | 1462 | .quit = gfio_quit_op, |
807f9971 | 1463 | .add_job = gfio_add_job_op, |
ed727a46 | 1464 | .timed_out = gfio_client_timed_out, |
6b79c80c | 1465 | .stop = gfio_client_stop, |
3ec62ec4 | 1466 | .stay_connected = 1, |
a1820207 SC |
1467 | }; |
1468 | ||
ff1f3280 SC |
1469 | static void quit_clicked(__attribute__((unused)) GtkWidget *widget, |
1470 | __attribute__((unused)) gpointer data) | |
1471 | { | |
1472 | gtk_main_quit(); | |
1473 | } | |
1474 | ||
25927259 SC |
1475 | static void *job_thread(void *arg) |
1476 | { | |
a9eccde4 JA |
1477 | struct gui *ui = arg; |
1478 | ||
1479 | ui->handler_running = 1; | |
25927259 | 1480 | fio_handle_clients(&gfio_client_ops); |
a9eccde4 | 1481 | ui->handler_running = 0; |
25927259 SC |
1482 | return NULL; |
1483 | } | |
1484 | ||
2f99deb0 | 1485 | static int send_job_files(struct gui_entry *ge) |
60f6b330 | 1486 | { |
9988ca70 | 1487 | struct gfio_client *gc = ge->client; |
441013b4 | 1488 | int i, ret = 0; |
0420ba6a | 1489 | |
2f99deb0 | 1490 | for (i = 0; i < ge->nr_job_files; i++) { |
9988ca70 | 1491 | ret = fio_client_send_ini(gc->client, ge->job_files[i]); |
c724926b JA |
1492 | if (ret < 0) { |
1493 | GError *error; | |
1494 | ||
1495 | error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret)); | |
1496 | report_error(error); | |
1497 | g_error_free(error); | |
1498 | break; | |
1499 | } else if (ret) | |
441013b4 JA |
1500 | break; |
1501 | ||
2f99deb0 JA |
1502 | free(ge->job_files[i]); |
1503 | ge->job_files[i] = NULL; | |
441013b4 | 1504 | } |
2f99deb0 JA |
1505 | while (i < ge->nr_job_files) { |
1506 | free(ge->job_files[i]); | |
1507 | ge->job_files[i] = NULL; | |
441013b4 | 1508 | i++; |
0420ba6a JA |
1509 | } |
1510 | ||
441013b4 | 1511 | return ret; |
60f6b330 SC |
1512 | } |
1513 | ||
63a130b7 JA |
1514 | static void *server_thread(void *arg) |
1515 | { | |
1516 | is_backend = 1; | |
1517 | gfio_server_running = 1; | |
1518 | fio_start_server(NULL); | |
1519 | gfio_server_running = 0; | |
1520 | return NULL; | |
1521 | } | |
1522 | ||
2f99deb0 | 1523 | static void gfio_start_server(void) |
63a130b7 | 1524 | { |
2f99deb0 JA |
1525 | struct gui *ui = &main_ui; |
1526 | ||
63a130b7 JA |
1527 | if (!gfio_server_running) { |
1528 | gfio_server_running = 1; | |
1529 | pthread_create(&ui->server_t, NULL, server_thread, NULL); | |
e34f6ad7 | 1530 | pthread_detach(ui->server_t); |
63a130b7 JA |
1531 | } |
1532 | } | |
1533 | ||
f3074008 | 1534 | static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, |
25927259 | 1535 | gpointer data) |
f3074008 | 1536 | { |
2f99deb0 JA |
1537 | struct gui_entry *ge = data; |
1538 | struct gfio_client *gc = ge->client; | |
25927259 | 1539 | |
2f99deb0 JA |
1540 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0); |
1541 | fio_start_client(gc->client); | |
f3074008 SC |
1542 | } |
1543 | ||
df06f220 JA |
1544 | static void file_open(GtkWidget *w, gpointer data); |
1545 | ||
1546 | static void connect_clicked(GtkWidget *widget, gpointer data) | |
3e47bd25 | 1547 | { |
2f99deb0 JA |
1548 | struct gui_entry *ge = data; |
1549 | struct gfio_client *gc = ge->client; | |
3ec62ec4 | 1550 | |
2f99deb0 | 1551 | if (!ge->connected) { |
c724926b JA |
1552 | int ret; |
1553 | ||
2f99deb0 | 1554 | if (!ge->nr_job_files) |
df06f220 | 1555 | file_open(widget, data); |
2f99deb0 JA |
1556 | if (!ge->nr_job_files) |
1557 | return; | |
1558 | ||
1559 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running"); | |
1560 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
c724926b JA |
1561 | ret = fio_client_connect(gc->client); |
1562 | if (!ret) { | |
a9eccde4 JA |
1563 | if (!ge->ui->handler_running) |
1564 | pthread_create(&ge->ui->t, NULL, job_thread, ge->ui); | |
2f99deb0 JA |
1565 | gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0); |
1566 | gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1); | |
c724926b JA |
1567 | } else { |
1568 | GError *error; | |
1569 | ||
1570 | error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret)); | |
1571 | report_error(error); | |
1572 | g_error_free(error); | |
69406b92 | 1573 | } |
df06f220 | 1574 | } else { |
2f99deb0 JA |
1575 | fio_client_terminate(gc->client); |
1576 | gfio_set_connected(ge, 0); | |
1577 | clear_ge_ui_info(ge); | |
df06f220 | 1578 | } |
3e47bd25 JA |
1579 | } |
1580 | ||
b9d2f30a JA |
1581 | static void send_clicked(GtkWidget *widget, gpointer data) |
1582 | { | |
2f99deb0 | 1583 | struct gui_entry *ge = data; |
b9d2f30a | 1584 | |
2f99deb0 | 1585 | if (send_job_files(ge)) { |
c724926b JA |
1586 | GError *error; |
1587 | ||
1588 | 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); | |
1589 | report_error(error); | |
1590 | g_error_free(error); | |
1591 | ||
2f99deb0 | 1592 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1); |
b9d2f30a JA |
1593 | } |
1594 | ||
2f99deb0 JA |
1595 | gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0); |
1596 | gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1); | |
b9d2f30a JA |
1597 | } |
1598 | ||
2f99deb0 JA |
1599 | static GtkWidget *add_button(GtkWidget *buttonbox, |
1600 | struct button_spec *buttonspec, gpointer data) | |
f3074008 | 1601 | { |
2f99deb0 JA |
1602 | GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext); |
1603 | ||
1604 | g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data); | |
1605 | gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3); | |
1606 | gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext); | |
1607 | gtk_widget_set_sensitive(button, !buttonspec->start_insensitive); | |
1608 | ||
1609 | return button; | |
f3074008 SC |
1610 | } |
1611 | ||
2f99deb0 JA |
1612 | static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist, |
1613 | int nbuttons) | |
f3074008 SC |
1614 | { |
1615 | int i; | |
1616 | ||
f3074008 | 1617 | for (i = 0; i < nbuttons; i++) |
2f99deb0 | 1618 | ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge); |
f3074008 SC |
1619 | } |
1620 | ||
0420ba6a JA |
1621 | static void on_info_bar_response(GtkWidget *widget, gint response, |
1622 | gpointer data) | |
1623 | { | |
2f99deb0 JA |
1624 | struct gui *ui = &main_ui; |
1625 | ||
0420ba6a JA |
1626 | if (response == GTK_RESPONSE_OK) { |
1627 | gtk_widget_destroy(widget); | |
2f99deb0 | 1628 | ui->error_info_bar = NULL; |
0420ba6a JA |
1629 | } |
1630 | } | |
1631 | ||
df06f220 | 1632 | void report_error(GError *error) |
0420ba6a | 1633 | { |
2f99deb0 JA |
1634 | struct gui *ui = &main_ui; |
1635 | ||
1636 | if (ui->error_info_bar == NULL) { | |
1637 | ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, | |
0420ba6a JA |
1638 | GTK_RESPONSE_OK, |
1639 | NULL); | |
2f99deb0 JA |
1640 | g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); |
1641 | gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar), | |
0420ba6a JA |
1642 | GTK_MESSAGE_ERROR); |
1643 | ||
2f99deb0 JA |
1644 | ui->error_label = gtk_label_new(error->message); |
1645 | GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar)); | |
1646 | gtk_container_add(GTK_CONTAINER(container), ui->error_label); | |
0420ba6a | 1647 | |
2f99deb0 JA |
1648 | gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0); |
1649 | gtk_widget_show_all(ui->vbox); | |
0420ba6a JA |
1650 | } else { |
1651 | char buffer[256]; | |
1652 | snprintf(buffer, sizeof(buffer), "Failed to open file."); | |
2f99deb0 | 1653 | gtk_label_set(GTK_LABEL(ui->error_label), buffer); |
0420ba6a JA |
1654 | } |
1655 | } | |
1656 | ||
62bc937f JA |
1657 | struct connection_widgets |
1658 | { | |
1659 | GtkWidget *hentry; | |
1660 | GtkWidget *combo; | |
1661 | GtkWidget *button; | |
1662 | }; | |
1663 | ||
1664 | static void hostname_cb(GtkEntry *entry, gpointer data) | |
1665 | { | |
1666 | struct connection_widgets *cw = data; | |
1667 | int uses_net = 0, is_localhost = 0; | |
1668 | const gchar *text; | |
1669 | gchar *ctext; | |
1670 | ||
1671 | /* | |
1672 | * Check whether to display the 'auto start backend' box | |
1673 | * or not. Show it if we are a localhost and using network, | |
1674 | * or using a socket. | |
1675 | */ | |
1676 | ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo)); | |
1677 | if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4)) | |
1678 | uses_net = 1; | |
1679 | g_free(ctext); | |
1680 | ||
1681 | if (uses_net) { | |
1682 | text = gtk_entry_get_text(GTK_ENTRY(cw->hentry)); | |
1683 | if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") || | |
1684 | !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") || | |
1685 | !strcmp(text, "ip6-loopback")) | |
1686 | is_localhost = 1; | |
1687 | } | |
1688 | ||
1689 | if (!uses_net || is_localhost) { | |
1690 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1); | |
1691 | gtk_widget_set_sensitive(cw->button, 1); | |
1692 | } else { | |
1693 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0); | |
1694 | gtk_widget_set_sensitive(cw->button, 0); | |
1695 | } | |
1696 | } | |
1697 | ||
b9f3c7ed JA |
1698 | static int get_connection_details(char **host, int *port, int *type, |
1699 | int *server_start) | |
a7a42ce1 | 1700 | { |
62bc937f JA |
1701 | GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry; |
1702 | struct connection_widgets cw; | |
a7a42ce1 JA |
1703 | char *typeentry; |
1704 | ||
1705 | dialog = gtk_dialog_new_with_buttons("Connection details", | |
2f99deb0 | 1706 | GTK_WINDOW(main_ui.window), |
a7a42ce1 JA |
1707 | GTK_DIALOG_DESTROY_WITH_PARENT, |
1708 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
1709 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); | |
1710 | ||
1711 | frame = gtk_frame_new("Hostname / socket name"); | |
f129909e JA |
1712 | /* gtk_dialog_get_content_area() is 2.14 and newer */ |
1713 | vbox = GTK_DIALOG(dialog)->vbox; | |
a7a42ce1 JA |
1714 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); |
1715 | ||
1716 | box = gtk_vbox_new(FALSE, 6); | |
1717 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1718 | ||
1719 | hbox = gtk_hbox_new(TRUE, 10); | |
1720 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
62bc937f JA |
1721 | cw.hentry = gtk_entry_new(); |
1722 | gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost"); | |
1723 | gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0); | |
a7a42ce1 JA |
1724 | |
1725 | frame = gtk_frame_new("Port"); | |
1726 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
1727 | box = gtk_vbox_new(FALSE, 10); | |
1728 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1729 | ||
1730 | hbox = gtk_hbox_new(TRUE, 4); | |
1731 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
1732 | pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); | |
1733 | ||
1734 | frame = gtk_frame_new("Type"); | |
1735 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
1736 | box = gtk_vbox_new(FALSE, 10); | |
1737 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1738 | ||
1739 | hbox = gtk_hbox_new(TRUE, 4); | |
1740 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
1741 | ||
62bc937f JA |
1742 | cw.combo = gtk_combo_box_new_text(); |
1743 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4"); | |
1744 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6"); | |
1745 | gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket"); | |
1746 | gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0); | |
a7a42ce1 | 1747 | |
62bc937f | 1748 | gtk_container_add(GTK_CONTAINER(hbox), cw.combo); |
a7a42ce1 | 1749 | |
b9f3c7ed JA |
1750 | frame = gtk_frame_new("Options"); |
1751 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
1752 | box = gtk_vbox_new(FALSE, 10); | |
1753 | gtk_container_add(GTK_CONTAINER(frame), box); | |
1754 | ||
1755 | hbox = gtk_hbox_new(TRUE, 4); | |
1756 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
1757 | ||
62bc937f JA |
1758 | cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend"); |
1759 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1); | |
1760 | 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."); | |
1761 | gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6); | |
1762 | ||
1763 | /* | |
1764 | * Connect edit signal, so we can show/not-show the auto start button | |
1765 | */ | |
1766 | g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw); | |
1767 | g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw); | |
b9f3c7ed | 1768 | |
a7a42ce1 JA |
1769 | gtk_widget_show_all(dialog); |
1770 | ||
1771 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
1772 | gtk_widget_destroy(dialog); | |
1773 | return 1; | |
1774 | } | |
1775 | ||
62bc937f | 1776 | *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry))); |
a7a42ce1 JA |
1777 | *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); |
1778 | ||
62bc937f | 1779 | typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo)); |
a7a42ce1 JA |
1780 | if (!typeentry || !strncmp(typeentry, "IPv4", 4)) |
1781 | *type = Fio_client_ipv4; | |
1782 | else if (!strncmp(typeentry, "IPv6", 4)) | |
1783 | *type = Fio_client_ipv6; | |
1784 | else | |
1785 | *type = Fio_client_socket; | |
1786 | g_free(typeentry); | |
1787 | ||
62bc937f | 1788 | *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button)); |
b9f3c7ed | 1789 | |
a7a42ce1 JA |
1790 | gtk_widget_destroy(dialog); |
1791 | return 0; | |
1792 | } | |
1793 | ||
2f99deb0 | 1794 | static void gfio_client_added(struct gui_entry *ge, struct fio_client *client) |
e0681f3e JA |
1795 | { |
1796 | struct gfio_client *gc; | |
1797 | ||
1798 | gc = malloc(sizeof(*gc)); | |
1799 | memset(gc, 0, sizeof(*gc)); | |
2f99deb0 | 1800 | gc->ge = ge; |
343cb4a9 | 1801 | gc->client = fio_get_client(client); |
b9d2f30a | 1802 | |
2f99deb0 | 1803 | ge->client = gc; |
e0681f3e JA |
1804 | |
1805 | client->client_data = gc; | |
1806 | } | |
1807 | ||
2f99deb0 JA |
1808 | static GtkWidget *new_client_page(struct gui_entry *ge); |
1809 | ||
1810 | static struct gui_entry *alloc_new_gui_entry(struct gui *ui) | |
1811 | { | |
1812 | struct gui_entry *ge; | |
1813 | ||
1814 | ge = malloc(sizeof(*ge)); | |
1815 | memset(ge, 0, sizeof(*ge)); | |
1816 | INIT_FLIST_HEAD(&ge->list); | |
1817 | flist_add_tail(&ge->list, &ui->list); | |
1818 | ge->ui = ui; | |
1819 | return ge; | |
1820 | } | |
1821 | ||
1822 | /* | |
1823 | * FIXME: need more handling here | |
1824 | */ | |
1825 | static void ge_destroy(GtkWidget *w, gpointer data) | |
1826 | { | |
1827 | struct gui_entry *ge = data; | |
343cb4a9 JA |
1828 | struct gfio_client *gc = ge->client; |
1829 | ||
1830 | if (gc->client) | |
1831 | fio_put_client(gc->client); | |
2f99deb0 JA |
1832 | |
1833 | flist_del(&ge->list); | |
1834 | free(ge); | |
1835 | } | |
1836 | ||
1837 | static struct gui_entry *get_new_ge_with_tab(const char *name) | |
1838 | { | |
1839 | struct gui_entry *ge; | |
1840 | ||
1841 | ge = alloc_new_gui_entry(&main_ui); | |
1842 | ||
1843 | ge->vbox = new_client_page(ge); | |
1844 | g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge); | |
1845 | ||
1846 | ge->page_label = gtk_label_new(name); | |
1847 | ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label); | |
1848 | ||
1849 | gtk_widget_show_all(main_ui.window); | |
1850 | return ge; | |
1851 | } | |
1852 | ||
1853 | static void file_new(GtkWidget *w, gpointer data) | |
1854 | { | |
1855 | get_new_ge_with_tab("Untitled"); | |
1856 | } | |
1857 | ||
1858 | /* | |
1859 | * Return the 'ge' corresponding to the tab. If the active tab is the | |
1860 | * main tab, open a new tab. | |
1861 | */ | |
1862 | static struct gui_entry *get_ge_from_page(unsigned int cur_page) | |
1863 | { | |
1864 | struct flist_head *entry; | |
1865 | struct gui_entry *ge; | |
1866 | ||
1867 | if (!cur_page) | |
1868 | return get_new_ge_with_tab("Untitled"); | |
1869 | ||
1870 | flist_for_each(entry, &main_ui.list) { | |
1871 | ge = flist_entry(entry, struct gui_entry, list); | |
1872 | if (ge->page_num == cur_page) | |
1873 | return ge; | |
1874 | } | |
1875 | ||
1876 | return NULL; | |
1877 | } | |
1878 | ||
0420ba6a JA |
1879 | static void file_open(GtkWidget *w, gpointer data) |
1880 | { | |
63a130b7 | 1881 | struct gui *ui = data; |
2f99deb0 | 1882 | GtkWidget *dialog; |
0420ba6a JA |
1883 | GSList *filenames, *fn_glist; |
1884 | GtkFileFilter *filter; | |
a7a42ce1 | 1885 | char *host; |
b9f3c7ed | 1886 | int port, type, server_start; |
2f99deb0 JA |
1887 | struct gui_entry *ge; |
1888 | gint cur_page; | |
1889 | ||
1890 | /* | |
1891 | * Creates new tab if current tab is the main window, or the | |
1892 | * current tab already has a client. | |
1893 | */ | |
1894 | cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); | |
1895 | ge = get_ge_from_page(cur_page); | |
1896 | if (ge->client) | |
1897 | ge = get_new_ge_with_tab("Untitled"); | |
1898 | ||
1899 | gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); | |
0420ba6a JA |
1900 | |
1901 | dialog = gtk_file_chooser_dialog_new("Open File", | |
63a130b7 | 1902 | GTK_WINDOW(ui->window), |
0420ba6a JA |
1903 | GTK_FILE_CHOOSER_ACTION_OPEN, |
1904 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
1905 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
1906 | NULL); | |
1907 | gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | |
1908 | ||
1909 | filter = gtk_file_filter_new(); | |
1910 | gtk_file_filter_add_pattern(filter, "*.fio"); | |
1911 | gtk_file_filter_add_pattern(filter, "*.job"); | |
2d262990 | 1912 | gtk_file_filter_add_pattern(filter, "*.ini"); |
0420ba6a JA |
1913 | gtk_file_filter_add_mime_type(filter, "text/fio"); |
1914 | gtk_file_filter_set_name(filter, "Fio job file"); | |
1915 | gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); | |
1916 | ||
1917 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
1918 | gtk_widget_destroy(dialog); | |
1919 | return; | |
1920 | } | |
1921 | ||
1922 | fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | |
a7a42ce1 JA |
1923 | |
1924 | gtk_widget_destroy(dialog); | |
1925 | ||
b9f3c7ed | 1926 | if (get_connection_details(&host, &port, &type, &server_start)) |
a7a42ce1 JA |
1927 | goto err; |
1928 | ||
0420ba6a JA |
1929 | filenames = fn_glist; |
1930 | while (filenames != NULL) { | |
e0681f3e JA |
1931 | struct fio_client *client; |
1932 | ||
2f99deb0 JA |
1933 | ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *)); |
1934 | ge->job_files[ge->nr_job_files] = strdup(filenames->data); | |
1935 | ge->nr_job_files++; | |
0420ba6a | 1936 | |
e0681f3e JA |
1937 | client = fio_client_add_explicit(&gfio_client_ops, host, type, port); |
1938 | if (!client) { | |
df06f220 JA |
1939 | GError *error; |
1940 | ||
1941 | error = g_error_new(g_quark_from_string("fio"), 1, | |
1942 | "Failed to add client %s", host); | |
0420ba6a JA |
1943 | report_error(error); |
1944 | g_error_free(error); | |
0420ba6a | 1945 | } |
2f99deb0 | 1946 | gfio_client_added(ge, client); |
0420ba6a JA |
1947 | |
1948 | g_free(filenames->data); | |
1949 | filenames = g_slist_next(filenames); | |
1950 | } | |
a7a42ce1 | 1951 | free(host); |
63a130b7 JA |
1952 | |
1953 | if (server_start) | |
2f99deb0 | 1954 | gfio_start_server(); |
a7a42ce1 | 1955 | err: |
0420ba6a | 1956 | g_slist_free(fn_glist); |
0420ba6a JA |
1957 | } |
1958 | ||
1959 | static void file_save(GtkWidget *w, gpointer data) | |
1960 | { | |
63a130b7 | 1961 | struct gui *ui = data; |
0420ba6a JA |
1962 | GtkWidget *dialog; |
1963 | ||
1964 | dialog = gtk_file_chooser_dialog_new("Save File", | |
63a130b7 | 1965 | GTK_WINDOW(ui->window), |
0420ba6a JA |
1966 | GTK_FILE_CHOOSER_ACTION_SAVE, |
1967 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
1968 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
1969 | NULL); | |
1970 | ||
1971 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); | |
1972 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); | |
1973 | ||
1974 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | |
1975 | char *filename; | |
1976 | ||
1977 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
1978 | // save_job_file(filename); | |
1979 | g_free(filename); | |
1980 | } | |
1981 | gtk_widget_destroy(dialog); | |
1982 | } | |
1983 | ||
9b260bdf JA |
1984 | static void view_log_destroy(GtkWidget *w, gpointer data) |
1985 | { | |
1986 | struct gui *ui = (struct gui *) data; | |
1987 | ||
1988 | gtk_widget_ref(ui->log_tree); | |
1989 | gtk_container_remove(GTK_CONTAINER(w), ui->log_tree); | |
1990 | gtk_widget_destroy(w); | |
4cbe7211 | 1991 | ui->log_view = NULL; |
9b260bdf JA |
1992 | } |
1993 | ||
1994 | static void view_log(GtkWidget *w, gpointer data) | |
1995 | { | |
4cbe7211 JA |
1996 | GtkWidget *win, *scroll, *vbox, *box; |
1997 | struct gui *ui = (struct gui *) data; | |
9b260bdf | 1998 | |
4cbe7211 JA |
1999 | if (ui->log_view) |
2000 | return; | |
2001 | ||
2002 | ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
9b260bdf | 2003 | gtk_window_set_title(GTK_WINDOW(win), "Log"); |
4cbe7211 | 2004 | gtk_window_set_default_size(GTK_WINDOW(win), 700, 500); |
9b260bdf | 2005 | |
4cbe7211 JA |
2006 | scroll = gtk_scrolled_window_new(NULL, NULL); |
2007 | ||
2008 | gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); | |
2009 | ||
2010 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2011 | ||
2012 | box = gtk_hbox_new(TRUE, 0); | |
2013 | gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree); | |
2014 | g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui); | |
2015 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box); | |
2016 | ||
2017 | vbox = gtk_vbox_new(TRUE, 5); | |
2018 | gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll); | |
9b260bdf | 2019 | |
4cbe7211 | 2020 | gtk_container_add(GTK_CONTAINER(win), vbox); |
9b260bdf JA |
2021 | gtk_widget_show_all(win); |
2022 | } | |
2023 | ||
46974a7d JA |
2024 | static void preferences(GtkWidget *w, gpointer data) |
2025 | { | |
f3e8440f | 2026 | GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font; |
46974a7d JA |
2027 | int i; |
2028 | ||
2029 | dialog = gtk_dialog_new_with_buttons("Preferences", | |
2f99deb0 | 2030 | GTK_WINDOW(main_ui.window), |
46974a7d JA |
2031 | GTK_DIALOG_DESTROY_WITH_PARENT, |
2032 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
2033 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, | |
2034 | NULL); | |
2035 | ||
0b8d11ed | 2036 | frame = gtk_frame_new("Debug logging"); |
46974a7d | 2037 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); |
f3e8440f JA |
2038 | |
2039 | vbox = gtk_vbox_new(FALSE, 6); | |
2040 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
2041 | ||
46974a7d | 2042 | box = gtk_hbox_new(FALSE, 6); |
f3e8440f | 2043 | gtk_container_add(GTK_CONTAINER(vbox), box); |
46974a7d JA |
2044 | |
2045 | buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); | |
2046 | ||
2047 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
f3e8440f JA |
2048 | if (i == 7) { |
2049 | box = gtk_hbox_new(FALSE, 6); | |
2050 | gtk_container_add(GTK_CONTAINER(vbox), box); | |
2051 | } | |
2052 | ||
2053 | ||
46974a7d | 2054 | buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); |
0b8d11ed | 2055 | gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); |
46974a7d JA |
2056 | gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); |
2057 | } | |
2058 | ||
f3e8440f JA |
2059 | frame = gtk_frame_new("Graph font"); |
2060 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); | |
2061 | vbox = gtk_vbox_new(FALSE, 6); | |
2062 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
2063 | ||
2064 | font = gtk_font_button_new(); | |
2065 | gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5); | |
2066 | ||
46974a7d JA |
2067 | gtk_widget_show_all(dialog); |
2068 | ||
2069 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
2070 | gtk_widget_destroy(dialog); | |
2071 | return; | |
2072 | } | |
2073 | ||
2074 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
2075 | int set; | |
2076 | ||
2077 | set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); | |
2078 | if (set) | |
2079 | fio_debug |= (1UL << i); | |
2080 | } | |
2081 | ||
f3e8440f | 2082 | gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); |
46974a7d JA |
2083 | gtk_widget_destroy(dialog); |
2084 | } | |
2085 | ||
0420ba6a JA |
2086 | static void about_dialog(GtkWidget *w, gpointer data) |
2087 | { | |
81e4ea6e JA |
2088 | const char *authors[] = { |
2089 | "Jens Axboe <axboe@kernel.dk>", | |
2090 | "Stephen Carmeron <stephenmcameron@gmail.com>", | |
2091 | NULL | |
2092 | }; | |
84a72ed3 JA |
2093 | const char *license[] = { |
2094 | "Fio is free software; you can redistribute it and/or modify " | |
2095 | "it under the terms of the GNU General Public License as published by " | |
2096 | "the Free Software Foundation; either version 2 of the License, or " | |
2097 | "(at your option) any later version.\n", | |
2098 | "Fio is distributed in the hope that it will be useful, " | |
2099 | "but WITHOUT ANY WARRANTY; without even the implied warranty of " | |
2100 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " | |
2101 | "GNU General Public License for more details.\n", | |
2102 | "You should have received a copy of the GNU General Public License " | |
2103 | "along with Fio; if not, write to the Free Software Foundation, Inc., " | |
2104 | "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" | |
2105 | }; | |
2106 | char *license_trans; | |
2107 | ||
2108 | license_trans = g_strconcat(license[0], "\n", license[1], "\n", | |
2109 | license[2], "\n", NULL); | |
81e4ea6e | 2110 | |
0420ba6a JA |
2111 | gtk_show_about_dialog(NULL, |
2112 | "program-name", "gfio", | |
2113 | "comments", "Gtk2 UI for fio", | |
84a72ed3 | 2114 | "license", license_trans, |
81e4ea6e JA |
2115 | "website", "http://git.kernel.dk/?p=fio.git;a=summary", |
2116 | "authors", authors, | |
0420ba6a | 2117 | "version", fio_version_string, |
81e4ea6e | 2118 | "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>", |
0420ba6a JA |
2119 | "logo-icon-name", "fio", |
2120 | /* Must be last: */ | |
81e4ea6e | 2121 | "wrap-license", TRUE, |
0420ba6a | 2122 | NULL); |
84a72ed3 | 2123 | |
2f99deb0 | 2124 | g_free(license_trans); |
0420ba6a JA |
2125 | } |
2126 | ||
2127 | static GtkActionEntry menu_items[] = { | |
46974a7d | 2128 | { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, |
9b260bdf | 2129 | { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL}, |
46974a7d | 2130 | { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, |
2f99deb0 | 2131 | { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) }, |
46974a7d JA |
2132 | { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, |
2133 | { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, | |
2134 | { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, | |
9b260bdf | 2135 | { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) }, |
46974a7d JA |
2136 | { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, |
2137 | { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, | |
0420ba6a | 2138 | }; |
3e47bd25 | 2139 | static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); |
0420ba6a JA |
2140 | |
2141 | static const gchar *ui_string = " \ | |
2142 | <ui> \ | |
2143 | <menubar name=\"MainMenu\"> \ | |
2144 | <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ | |
2f99deb0 JA |
2145 | <menuitem name=\"New\" action=\"NewFile\" /> \ |
2146 | <separator name=\"Separator1\"/> \ | |
0420ba6a JA |
2147 | <menuitem name=\"Open\" action=\"OpenFile\" /> \ |
2148 | <menuitem name=\"Save\" action=\"SaveFile\" /> \ | |
46974a7d | 2149 | <separator name=\"Separator2\"/> \ |
2f99deb0 JA |
2150 | <menuitem name=\"Preferences\" action=\"Preferences\" /> \ |
2151 | <separator name=\"Separator3\"/> \ | |
0420ba6a JA |
2152 | <menuitem name=\"Quit\" action=\"Quit\" /> \ |
2153 | </menu> \ | |
9b260bdf JA |
2154 | <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \ |
2155 | <menuitem name=\"Log\" action=\"ViewLog\" /> \ | |
2156 | </menu>\ | |
0420ba6a JA |
2157 | <menu name=\"Help\" action=\"HelpMenuAction\"> \ |
2158 | <menuitem name=\"About\" action=\"About\" /> \ | |
2159 | </menu> \ | |
2160 | </menubar> \ | |
2161 | </ui> \ | |
2162 | "; | |
2163 | ||
4cbe7211 JA |
2164 | static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager, |
2165 | struct gui *ui) | |
0420ba6a JA |
2166 | { |
2167 | GtkActionGroup *action_group = gtk_action_group_new("Menu"); | |
2168 | GError *error = 0; | |
2169 | ||
2170 | action_group = gtk_action_group_new("Menu"); | |
4cbe7211 | 2171 | gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui); |
0420ba6a JA |
2172 | |
2173 | gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); | |
2174 | gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); | |
2175 | ||
2176 | gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); | |
2177 | return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); | |
2178 | } | |
2179 | ||
2180 | void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, | |
2181 | GtkWidget *vbox, GtkUIManager *ui_manager) | |
2182 | { | |
2183 | gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); | |
2184 | } | |
2185 | ||
2f99deb0 | 2186 | static GtkWidget *new_client_page(struct gui_entry *ge) |
ff1f3280 | 2187 | { |
2f99deb0 | 2188 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; |
aaa71f68 | 2189 | GdkColor white; |
0420ba6a | 2190 | |
2f99deb0 | 2191 | main_vbox = gtk_vbox_new(FALSE, 3); |
45032dd8 | 2192 | |
2f99deb0 JA |
2193 | ge->topalign = gtk_alignment_new(0, 0, 1, 0); |
2194 | ge->topvbox = gtk_vbox_new(FALSE, 3); | |
2195 | gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox); | |
2196 | gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0); | |
2197 | ||
2198 | probe = gtk_frame_new("Job"); | |
2199 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
2200 | probe_frame = gtk_vbox_new(FALSE, 3); | |
2201 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
2202 | ||
2203 | probe_box = gtk_hbox_new(FALSE, 3); | |
2204 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2205 | ge->probe.hostname = new_info_label_in_frame(probe_box, "Host"); | |
2206 | ge->probe.os = new_info_label_in_frame(probe_box, "OS"); | |
2207 | ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); | |
2208 | ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); | |
2209 | ||
2210 | probe_box = gtk_hbox_new(FALSE, 3); | |
2211 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2212 | ||
3863d1ad | 2213 | ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs"); |
2f99deb0 JA |
2214 | ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO"); |
2215 | ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine"); | |
2216 | ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth"); | |
2217 | ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs"); | |
2218 | ge->eta.files = new_info_entry_in_frame(probe_box, "Open files"); | |
2219 | ||
2220 | probe_box = gtk_hbox_new(FALSE, 3); | |
2221 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
2222 | ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); | |
2223 | ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
2224 | ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); | |
2225 | ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
2226 | ||
2227 | /* | |
2228 | * Only add this if we have a commit rate | |
2839f0c6 | 2229 | */ |
2f99deb0 JA |
2230 | #if 0 |
2231 | probe_box = gtk_hbox_new(FALSE, 3); | |
2232 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
2839f0c6 | 2233 | |
2f99deb0 JA |
2234 | ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
2235 | ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
ff1f3280 | 2236 | |
2f99deb0 JA |
2237 | ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
2238 | ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
2239 | #endif | |
ff1f3280 | 2240 | |
2f99deb0 JA |
2241 | /* |
2242 | * Set up a drawing area and IOPS and bandwidth graphs | |
2243 | */ | |
2244 | gdk_color_parse("white", &white); | |
2245 | ge->graphs.drawing_area = gtk_drawing_area_new(); | |
2246 | ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM; | |
2247 | ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM; | |
2248 | gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area), | |
2249 | ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim); | |
2250 | gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white); | |
2251 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event", | |
2252 | G_CALLBACK(on_expose_drawing_area), &ge->graphs); | |
2253 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event", | |
2254 | G_CALLBACK(on_config_drawing_area), &ge->graphs); | |
2255 | ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL); | |
2256 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window), | |
2257 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2258 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window), | |
2259 | ge->graphs.drawing_area); | |
2260 | gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window, | |
2261 | TRUE, TRUE, 0); | |
04cc6b77 | 2262 | |
2f99deb0 JA |
2263 | setup_graphs(&ge->graphs); |
2264 | ||
2265 | /* | |
2266 | * Set up alignments for widgets at the bottom of ui, | |
2267 | * align bottom left, expand horizontally but not vertically | |
2268 | */ | |
2269 | ge->bottomalign = gtk_alignment_new(0, 1, 1, 0); | |
2270 | ge->buttonbox = gtk_hbox_new(FALSE, 0); | |
2271 | gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox); | |
2272 | gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign, | |
2273 | FALSE, FALSE, 0); | |
2274 | ||
2275 | add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist)); | |
0420ba6a | 2276 | |
c36f98d9 | 2277 | /* |
2f99deb0 JA |
2278 | * Set up thread status progress bar |
2279 | */ | |
2280 | ge->thread_status_pb = gtk_progress_bar_new(); | |
2281 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
2282 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections"); | |
2283 | gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb); | |
2284 | ||
2285 | ||
2286 | return main_vbox; | |
2287 | } | |
2288 | ||
2289 | static GtkWidget *new_main_page(struct gui *ui) | |
2290 | { | |
2291 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; | |
2292 | GdkColor white; | |
2293 | ||
2294 | main_vbox = gtk_vbox_new(FALSE, 3); | |
2295 | ||
2296 | /* | |
2297 | * Set up alignments for widgets at the top of ui, | |
c36f98d9 SC |
2298 | * align top left, expand horizontally but not vertically |
2299 | */ | |
2300 | ui->topalign = gtk_alignment_new(0, 0, 1, 0); | |
2f99deb0 | 2301 | ui->topvbox = gtk_vbox_new(FALSE, 0); |
c36f98d9 | 2302 | gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox); |
2f99deb0 | 2303 | gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0); |
c36f98d9 | 2304 | |
2f99deb0 JA |
2305 | probe = gtk_frame_new("Run statistics"); |
2306 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
843ad237 JA |
2307 | probe_frame = gtk_vbox_new(FALSE, 3); |
2308 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
2309 | ||
2310 | probe_box = gtk_hbox_new(FALSE, 3); | |
2f99deb0 | 2311 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); |
3863d1ad | 2312 | ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running"); |
ca850992 JA |
2313 | ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW"); |
2314 | ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
2315 | ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW"); | |
2316 | ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS"); | |
3e47bd25 | 2317 | |
807f9971 JA |
2318 | /* |
2319 | * Only add this if we have a commit rate | |
2320 | */ | |
2321 | #if 0 | |
3e47bd25 JA |
2322 | probe_box = gtk_hbox_new(FALSE, 3); |
2323 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
807f9971 JA |
2324 | |
2325 | ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); | |
2326 | ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
2327 | ||
3e47bd25 JA |
2328 | ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
2329 | ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
807f9971 | 2330 | #endif |
3e47bd25 | 2331 | |
736f2dff | 2332 | /* |
2fd3bb0e | 2333 | * Set up a drawing area and IOPS and bandwidth graphs |
736f2dff | 2334 | */ |
aaa71f68 | 2335 | gdk_color_parse("white", &white); |
2f99deb0 JA |
2336 | ui->graphs.drawing_area = gtk_drawing_area_new(); |
2337 | ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM; | |
2338 | ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM; | |
2339 | gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area), | |
2340 | ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim); | |
2341 | gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white); | |
2342 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event", | |
2343 | G_CALLBACK(on_expose_drawing_area), &ui->graphs); | |
2344 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event", | |
2345 | G_CALLBACK(on_config_drawing_area), &ui->graphs); | |
736f2dff SC |
2346 | ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
2347 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window), | |
2348 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2fd3bb0e | 2349 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window), |
2f99deb0 JA |
2350 | ui->graphs.drawing_area); |
2351 | gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window, | |
e164534f | 2352 | TRUE, TRUE, 0); |
736f2dff | 2353 | |
2f99deb0 | 2354 | setup_graphs(&ui->graphs); |
2fd3bb0e | 2355 | |
c36f98d9 SC |
2356 | /* |
2357 | * Set up alignments for widgets at the bottom of ui, | |
2358 | * align bottom left, expand horizontally but not vertically | |
2359 | */ | |
2360 | ui->bottomalign = gtk_alignment_new(0, 1, 1, 0); | |
2361 | ui->buttonbox = gtk_hbox_new(FALSE, 0); | |
2362 | gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox); | |
2f99deb0 | 2363 | gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign, |
e164534f | 2364 | FALSE, FALSE, 0); |
c36f98d9 | 2365 | |
3ec62ec4 JA |
2366 | /* |
2367 | * Set up thread status progress bar | |
2368 | */ | |
2369 | ui->thread_status_pb = gtk_progress_bar_new(); | |
2370 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); | |
8663ea65 | 2371 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); |
3ec62ec4 JA |
2372 | gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); |
2373 | ||
2f99deb0 JA |
2374 | return main_vbox; |
2375 | } | |
2376 | ||
2377 | static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget, | |
2378 | guint page, gpointer data) | |
2379 | ||
2380 | { | |
2381 | return TRUE; | |
2382 | } | |
2383 | ||
2384 | static void init_ui(int *argc, char **argv[], struct gui *ui) | |
2385 | { | |
2386 | GtkSettings *settings; | |
2387 | GtkUIManager *uimanager; | |
2388 | GtkWidget *menu, *vbox; | |
2389 | ||
2390 | /* Magical g*thread incantation, you just need this thread stuff. | |
2391 | * Without it, the update that happens in gfio_update_thread_status | |
2392 | * doesn't really happen in a timely fashion, you need expose events | |
2393 | */ | |
2394 | if (!g_thread_supported()) | |
2395 | g_thread_init(NULL); | |
2396 | gdk_threads_init(); | |
2397 | ||
2398 | gtk_init(argc, argv); | |
2399 | settings = gtk_settings_get_default(); | |
2400 | gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); | |
2401 | g_type_init(); | |
2402 | ||
2403 | ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
2404 | gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); | |
2405 | gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768); | |
2406 | ||
2407 | g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL); | |
2408 | g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL); | |
2409 | ||
2410 | ui->vbox = gtk_vbox_new(FALSE, 0); | |
2411 | gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox); | |
2412 | ||
2413 | uimanager = gtk_ui_manager_new(); | |
2414 | menu = get_menubar_menu(ui->window, uimanager, ui); | |
2415 | gfio_ui_setup(settings, menu, ui->vbox, uimanager); | |
2416 | ||
2417 | ui->notebook = gtk_notebook_new(); | |
2418 | g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui); | |
b870c31b | 2419 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1); |
0aa928c4 | 2420 | gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook)); |
2f99deb0 JA |
2421 | gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook); |
2422 | ||
2423 | vbox = new_main_page(ui); | |
2424 | ||
2425 | gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main")); | |
2426 | ||
9b260bdf | 2427 | gfio_ui_setup_log(ui); |
3ec62ec4 | 2428 | |
ff1f3280 SC |
2429 | gtk_widget_show_all(ui->window); |
2430 | } | |
2431 | ||
8232e285 | 2432 | int main(int argc, char *argv[], char *envp[]) |
ff1f3280 | 2433 | { |
8232e285 SC |
2434 | if (initialize_fio(envp)) |
2435 | return 1; | |
0420ba6a JA |
2436 | if (fio_init_options()) |
2437 | return 1; | |
a1820207 | 2438 | |
2f99deb0 JA |
2439 | memset(&main_ui, 0, sizeof(main_ui)); |
2440 | INIT_FLIST_HEAD(&main_ui.list); | |
2441 | ||
2442 | init_ui(&argc, &argv, &main_ui); | |
5b7573ab | 2443 | |
2839f0c6 | 2444 | gdk_threads_enter(); |
ff1f3280 | 2445 | gtk_main(); |
2839f0c6 | 2446 | gdk_threads_leave(); |
ff1f3280 SC |
2447 | return 0; |
2448 | } |