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