gfio: Change some labels to entries and add bw stats to final output
[fio.git] / gfio.c
CommitLineData
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>
5 *
6 * The license below covers all files distributed with fio unless otherwise
7 * noted in the file itself.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
8232e285 23#include <locale.h>
60f6b330 24#include <malloc.h>
8232e285 25
5b7573ab 26#include <glib.h>
ff1f3280
SC
27#include <gtk/gtk.h>
28
8232e285
SC
29#include "fio.h"
30
3e47bd25
JA
31static void gfio_update_thread_status(char *status_message, double perc);
32
f3074008
SC
33#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
3e47bd25 37static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
38static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40static struct button_spec {
41 const char *buttontext;
42 clickfunction f;
43 const char *tooltiptext;
3e47bd25 44 const int start_insensitive;
f3074008 45} buttonspeclist[] = {
3e47bd25
JA
46#define CONNECT_BUTTON 0
47#define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
f3074008
SC
49 { "Start Job",
50 start_job_clicked,
3e47bd25 51 "Send current fio job to fio server to be executed", 1 },
f3074008
SC
52};
53
843ad237
JA
54struct probe_widget {
55 GtkWidget *hostname;
56 GtkWidget *os;
57 GtkWidget *arch;
58 GtkWidget *fio_ver;
59};
60
3e47bd25 61struct eta_widget {
807f9971
JA
62 GtkWidget *name;
63 GtkWidget *iotype;
64 GtkWidget *ioengine;
65 GtkWidget *iodepth;
3e47bd25
JA
66 GtkWidget *jobs;
67 GtkWidget *files;
68 GtkWidget *read_bw;
69 GtkWidget *read_iops;
70 GtkWidget *cr_bw;
71 GtkWidget *cr_iops;
72 GtkWidget *write_bw;
73 GtkWidget *write_iops;
74 GtkWidget *cw_bw;
75 GtkWidget *cw_iops;
76};
77
ff1f3280
SC
78struct gui {
79 GtkWidget *window;
5b7573ab 80 GtkWidget *vbox;
c36f98d9
SC
81 GtkWidget *topvbox;
82 GtkWidget *topalign;
83 GtkWidget *bottomalign;
04cc6b77 84 GtkWidget *thread_status_pb;
f3074008
SC
85 GtkWidget *buttonbox;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
736f2dff
SC
87 GtkWidget *scrolled_window;
88 GtkWidget *textview;
0420ba6a
JA
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
736f2dff 91 GtkTextBuffer *text;
843ad237 92 struct probe_widget probe;
3e47bd25 93 struct eta_widget eta;
3ec62ec4 94 int connected;
25927259 95 pthread_t t;
0420ba6a 96
3ec62ec4 97 struct fio_client *client;
0420ba6a
JA
98 int nr_job_files;
99 char **job_files;
5b7573ab 100} ui;
ff1f3280 101
8663ea65
JA
102static void clear_ui_info(struct gui *ui)
103{
104 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
105 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
106 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
107 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
ca850992
JA
108 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
109 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
110 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
111 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
112 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
113 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
114 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
115 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
116 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
117 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
8663ea65
JA
118}
119
3650a3ca
JA
120static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
121{
122 GtkWidget *entry, *frame;
123
124 frame = gtk_frame_new(label);
125 entry = gtk_entry_new();
1c1e4a5b 126 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
3650a3ca
JA
127 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
128 gtk_container_add(GTK_CONTAINER(frame), entry);
129
130 return entry;
131}
132
133static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
134{
135 GtkWidget *label_widget;
136 GtkWidget *frame;
137
138 frame = gtk_frame_new(label);
139 label_widget = gtk_label_new(NULL);
140 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
141 gtk_container_add(GTK_CONTAINER(frame), label_widget);
142
143 return label_widget;
144}
145
146static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
147{
148 GtkWidget *button, *box;
149
150 box = gtk_hbox_new(FALSE, 3);
151 gtk_container_add(GTK_CONTAINER(hbox), box);
152
153 button = gtk_spin_button_new_with_range(min, max, 1.0);
154 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
155
156 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
157 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
158
159 return button;
160}
161
3ec62ec4
JA
162static void gfio_set_connected(struct gui *ui, int connected)
163{
164 if (connected) {
165 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
166 ui->connected = 1;
167 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
168 } else {
169 ui->connected = 0;
170 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
171 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
172 }
173}
174
3650a3ca
JA
175static void label_set_int_value(GtkWidget *entry, unsigned int val)
176{
177 char tmp[80];
178
179 sprintf(tmp, "%u", val);
180 gtk_label_set_text(GTK_LABEL(entry), tmp);
181}
182
183static void entry_set_int_value(GtkWidget *entry, unsigned int val)
184{
185 char tmp[80];
186
187 sprintf(tmp, "%u", val);
188 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
189}
190
a269790c
JA
191#define ALIGN_LEFT 1
192#define ALIGN_RIGHT 2
193#define INVISIBLE 4
194#define UNSORTABLE 8
195
196GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
197{
198 GtkCellRenderer *renderer;
199 GtkTreeViewColumn *col;
200 double xalign = 0.0; /* left as default */
201 PangoAlignment align;
202 gboolean visible;
203
204 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
205 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
206 PANGO_ALIGN_CENTER;
207 visible = !(flags & INVISIBLE);
208
209 renderer = gtk_cell_renderer_text_new();
210 col = gtk_tree_view_column_new();
211
212 gtk_tree_view_column_set_title(col, title);
213 if (!(flags & UNSORTABLE))
214 gtk_tree_view_column_set_sort_column_id(col, index);
215 gtk_tree_view_column_set_resizable(col, TRUE);
216 gtk_tree_view_column_pack_start(col, renderer, TRUE);
217 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
218 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
219 switch (align) {
220 case PANGO_ALIGN_LEFT:
221 xalign = 0.0;
222 break;
223 case PANGO_ALIGN_CENTER:
224 xalign = 0.5;
225 break;
226 case PANGO_ALIGN_RIGHT:
227 xalign = 1.0;
228 break;
229 }
230 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
231 gtk_tree_view_column_set_visible(col, visible);
232 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
233 return col;
234}
235
236static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
237 fio_fp64_t *plist,
238 unsigned int len,
239 const char *base,
240 unsigned int scale)
241{
242 GType types[FIO_IO_U_LIST_MAX_LEN];
243 GtkWidget *tree_view;
244 GtkTreeSelection *selection;
245 GtkListStore *model;
246 GtkTreeIter iter;
247 int i;
248
249 for (i = 0; i < len; i++)
250 types[i] = G_TYPE_INT;
251
252 model = gtk_list_store_newv(len, types);
253
254 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
255 gtk_widget_set_can_focus(tree_view, FALSE);
256
257 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
258 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
259
260 for (i = 0; i < len; i++) {
261 char fbuf[8];
262
263 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
264 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
265 }
266
267 gtk_list_store_append(model, &iter);
268
269 for (i = 0; i < len; i++)
270 gtk_list_store_set(model, &iter, i, ovals[i], -1);
271
272 return tree_view;
273}
274
275static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
276 int ddir)
277{
278 unsigned int *io_u_plat = ts->io_u_plat[ddir];
279 unsigned long nr = ts->clat_stat[ddir].samples;
280 fio_fp64_t *plist = ts->percentile_list;
281 unsigned int *ovals, len, minv, maxv, scale_down;
282 const char *base;
283 GtkWidget *tree_view, *frame, *hbox;
284 char tmp[64];
285
286 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
287 if (!len)
288 goto out;
289
290 /*
291 * We default to usecs, but if the value range is such that we
292 * should scale down to msecs, do that.
293 */
294 if (minv > 2000 && maxv > 99999) {
295 scale_down = 1;
296 base = "msec";
297 } else {
298 scale_down = 0;
299 base = "usec";
300 }
301
302 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
303
304 sprintf(tmp, "Completion percentiles (%s)", base);
305 frame = gtk_frame_new(tmp);
306 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
307
308 hbox = gtk_hbox_new(FALSE, 3);
309 gtk_container_add(GTK_CONTAINER(frame), hbox);
310
311 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
312out:
313 if (ovals)
314 free(ovals);
315}
316
3650a3ca
JA
317static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
318 unsigned long max, double mean, double dev)
319{
320 const char *base = "(usec)";
1c1e4a5b 321 GtkWidget *hbox, *label, *frame;
3650a3ca
JA
322 char *minp, *maxp;
323 char tmp[64];
324
325 if (!usec_to_msec(&min, &max, &mean, &dev))
326 base = "(msec)";
327
328 minp = num2str(min, 6, 1, 0);
329 maxp = num2str(max, 6, 1, 0);
330
3650a3ca
JA
331 sprintf(tmp, "%s %s", name, base);
332 frame = gtk_frame_new(tmp);
333 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
334
3650a3ca 335 hbox = gtk_hbox_new(FALSE, 3);
1c1e4a5b 336 gtk_container_add(GTK_CONTAINER(frame), hbox);
3650a3ca
JA
337
338 label = new_info_label_in_frame(hbox, "Minimum");
339 gtk_label_set_text(GTK_LABEL(label), minp);
340 label = new_info_label_in_frame(hbox, "Maximum");
341 gtk_label_set_text(GTK_LABEL(label), maxp);
342 label = new_info_label_in_frame(hbox, "Average");
343 sprintf(tmp, "%5.02f", mean);
344 gtk_label_set_text(GTK_LABEL(label), tmp);
345 label = new_info_label_in_frame(hbox, "Standard deviation");
346 sprintf(tmp, "%5.02f", dev);
347 gtk_label_set_text(GTK_LABEL(label), tmp);
348
349 free(minp);
350 free(maxp);
351
352}
353
ca850992
JA
354#define GFIO_CLAT 1
355#define GFIO_SLAT 2
356#define GFIO_LAT 4
357
3650a3ca
JA
358static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
359 struct thread_stat *ts, int ddir)
360{
361 const char *ddir_label[2] = { "Read", "Write" };
362 GtkWidget *frame, *label, *box, *vbox;
363 unsigned long min, max, runt;
364 unsigned long long bw, iops;
ca850992 365 unsigned int flags = 0;
3650a3ca
JA
366 double mean, dev;
367 char *io_p, *bw_p, *iops_p;
368 int i2p;
369
370 if (!ts->runtime[ddir])
371 return;
372
373 i2p = is_power_of_2(rs->kb_base);
374 runt = ts->runtime[ddir];
375
376 bw = (1000 * ts->io_bytes[ddir]) / runt;
377 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
378 bw_p = num2str(bw, 6, 1, i2p);
379
380 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
381 iops_p = num2str(iops, 6, 1, 0);
382
383 box = gtk_hbox_new(FALSE, 3);
384 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
385
386 frame = gtk_frame_new(ddir_label[ddir]);
387 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
388
389 vbox = gtk_vbox_new(FALSE, 3);
390 gtk_container_add(GTK_CONTAINER(frame), vbox);
391
392 box = gtk_hbox_new(FALSE, 3);
393 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3);
394
395 label = new_info_label_in_frame(box, "IO");
396 gtk_label_set_text(GTK_LABEL(label), io_p);
397 label = new_info_label_in_frame(box, "Bandwidth");
398 gtk_label_set_text(GTK_LABEL(label), bw_p);
399 label = new_info_label_in_frame(box, "IOPS");
400 gtk_label_set_text(GTK_LABEL(label), iops_p);
401 label = new_info_label_in_frame(box, "Runtime (msec)");
402 label_set_int_value(label, ts->runtime[ddir]);
403
3650a3ca 404 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
ca850992 405 flags |= GFIO_SLAT;
3650a3ca 406 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
ca850992 407 flags |= GFIO_CLAT;
3650a3ca 408 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
ca850992
JA
409 flags |= GFIO_LAT;
410
411 if (flags) {
412 frame = gtk_frame_new("Latency");
413 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
414
415 vbox = gtk_vbox_new(FALSE, 3);
416 gtk_container_add(GTK_CONTAINER(frame), vbox);
417
418 if (flags & GFIO_SLAT)
419 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
420 if (flags & GFIO_CLAT)
421 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
422 if (flags & GFIO_LAT)
423 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
424 }
425
a269790c
JA
426 if (ts->clat_percentiles)
427 gfio_show_clat_percentiles(vbox, ts, ddir);
3650a3ca 428
ca850992
JA
429 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
430 double p_of_agg = 100.0;
431 const char *bw_str = "KB";
432 char tmp[32];
433
434 if (rs->agg[ddir]) {
435 p_of_agg = mean * 100 / (double) rs->agg[ddir];
436 if (p_of_agg > 100.0)
437 p_of_agg = 100.0;
438 }
439
440 if (mean > 999999.9) {
441 min /= 1000.0;
442 max /= 1000.0;
443 mean /= 1000.0;
444 dev /= 1000.0;
445 bw_str = "MB";
446 }
447
448 frame = gtk_frame_new("Bandwidth");
449 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
450
451 vbox = gtk_vbox_new(FALSE, 3);
452 gtk_container_add(GTK_CONTAINER(frame), vbox);
453
454 label = new_info_label_in_frame(vbox, "Bandwidth");
455 gtk_label_set_text(GTK_LABEL(label), bw_str);
456 label = new_info_label_in_frame(vbox, "Minimum");
457 label_set_int_value(label, min);
458 label = new_info_label_in_frame(vbox, "Maximum");
459 label_set_int_value(label, max);
460 label = new_info_label_in_frame(vbox, "Percentage of jobs");
461 sprintf(tmp, "%3.2f%%", p_of_agg);
462 gtk_label_set_text(GTK_LABEL(label), tmp);
463 label = new_info_label_in_frame(vbox, "Average");
464 sprintf(tmp, "%5.02f", mean);
465 gtk_label_set_text(GTK_LABEL(label), tmp);
466 label = new_info_label_in_frame(vbox, "Standard deviation");
467 sprintf(tmp, "%5.02f", dev);
468 gtk_label_set_text(GTK_LABEL(label), tmp);
469 }
470
3650a3ca
JA
471 free(io_p);
472 free(bw_p);
473 free(iops_p);
474}
475
476static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
477 struct group_run_stats *rs)
478{
479 GtkWidget *dialog, *box, *vbox, *entry, *content;
480 struct gui *ui = client->client_data;
481
482 gdk_threads_enter();
483
484 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
485 GTK_DIALOG_DESTROY_WITH_PARENT,
486 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
487
488 g_signal_connect_swapped(dialog, "response",
489 G_CALLBACK(gtk_widget_destroy),
490 dialog);
491
492 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
493
494 vbox = gtk_vbox_new(FALSE, 3);
495 gtk_container_add(GTK_CONTAINER(content), vbox);
496
497 box = gtk_hbox_new(TRUE, 3);
498 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
499
500 entry = new_info_entry_in_frame(box, "Name");
501 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
502 if (strlen(ts->description)) {
503 entry = new_info_entry_in_frame(box, "Description");
504 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
505 }
506 entry = new_info_entry_in_frame(box, "Group ID");
507 entry_set_int_value(entry, ts->groupid);
508 entry = new_info_entry_in_frame(box, "Jobs");
509 entry_set_int_value(entry, ts->members);
510 entry = new_info_entry_in_frame(box, "Error");
511 entry_set_int_value(entry, ts->error);
512 entry = new_info_entry_in_frame(box, "PID");
513 entry_set_int_value(entry, ts->pid);
514
515 if (ts->io_bytes[DDIR_READ])
516 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
517 if (ts->io_bytes[DDIR_WRITE])
518 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
519
520 gtk_widget_show_all(dialog);
521
522 gdk_threads_leave();
523}
524
084d1c6f 525static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 526{
807f9971 527#if 0
736f2dff
SC
528 GtkTextBuffer *buffer;
529 GtkTextIter end;
530
531 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
532 gdk_threads_enter();
533 gtk_text_buffer_get_end_iter(buffer, &end);
534 gtk_text_buffer_insert(buffer, &end, buf, -1);
535 gdk_threads_leave();
536 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
537 &end, 0.0, FALSE, 0.0,0.0);
807f9971 538#else
084d1c6f 539 fio_client_ops.text_op(client, cmd);
807f9971 540#endif
a1820207
SC
541}
542
543static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
544{
545 printf("gfio_disk_util_op called\n");
546 fio_client_ops.disk_util(client, cmd);
547}
548
3650a3ca
JA
549extern int sum_stat_clients;
550extern struct thread_stat client_ts;
551extern struct group_run_stats client_gs;
552
553static int sum_stat_nr;
554
89e5fad9
JA
555static void gfio_thread_status_op(struct fio_client *client,
556 struct fio_net_cmd *cmd)
a1820207 557{
3650a3ca
JA
558 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
559
560 gfio_display_ts(client, &p->ts, &p->rs);
561
562 if (sum_stat_clients == 1)
563 return;
564
565 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
566 sum_group_stats(&client_gs, &p->rs);
567
568 client_ts.members++;
569 client_ts.groupid = p->ts.groupid;
570
571 if (++sum_stat_nr == sum_stat_clients) {
572 strcpy(client_ts.name, "All clients");
573 gfio_display_ts(client, &client_ts, &client_gs);
574 }
a1820207
SC
575}
576
89e5fad9
JA
577static void gfio_group_stats_op(struct fio_client *client,
578 struct fio_net_cmd *cmd)
a1820207
SC
579{
580 printf("gfio_group_stats_op called\n");
89e5fad9 581 fio_client_ops.group_stats(client, cmd);
a1820207
SC
582}
583
3e47bd25
JA
584static void gfio_update_eta(struct jobs_eta *je)
585{
586 static int eta_good;
587 char eta_str[128];
588 char output[256];
589 char tmp[32];
590 double perc = 0.0;
591 int i2p = 0;
592
593 eta_str[0] = '\0';
594 output[0] = '\0';
595
596 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
597 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
598 eta_to_str(eta_str, je->eta_sec);
599 }
600
601 sprintf(tmp, "%u", je->nr_running);
ca850992 602 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
3e47bd25 603 sprintf(tmp, "%u", je->files_open);
ca850992 604 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
3e47bd25
JA
605
606#if 0
607 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
608 if (je->m_rate || je->t_rate) {
609 char *tr, *mr;
610
611 mr = num2str(je->m_rate, 4, 0, i2p);
612 tr = num2str(je->t_rate, 4, 0, i2p);
ca850992 613 gtk_entry_set_text(GTK_ENTRY(ui.eta);
3e47bd25
JA
614 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
615 free(tr);
616 free(mr);
617 } else if (je->m_iops || je->t_iops)
618 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 619
ca850992
JA
620 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
621 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
622 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
623 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
3e47bd25
JA
624#endif
625
626 if (je->eta_sec != INT_MAX && je->nr_running) {
627 char *iops_str[2];
628 char *rate_str[2];
629
630 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
631 strcpy(output, "-.-% done");
632 else {
633 eta_good = 1;
634 perc *= 100.0;
635 sprintf(output, "%3.1f%% done", perc);
636 }
637
638 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
639 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
640
641 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
642 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
643
ca850992
JA
644 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
645 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
646 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
647 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
3e47bd25
JA
648
649 free(rate_str[0]);
650 free(rate_str[1]);
651 free(iops_str[0]);
652 free(iops_str[1]);
653 }
654
655 if (eta_str[0]) {
656 char *dst = output + strlen(output);
657
658 sprintf(dst, " - %s", eta_str);
659 }
660
661 gfio_update_thread_status(output, perc);
662}
663
a1820207
SC
664static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
665{
843ad237
JA
666 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
667 const char *os, *arch;
668 char buf[64];
669
670 os = fio_get_os_string(probe->os);
671 if (!os)
672 os = "unknown";
673
674 arch = fio_get_arch_string(probe->arch);
675 if (!arch)
676 os = "unknown";
677
678 if (!client->name)
679 client->name = strdup((char *) probe->hostname);
680
681 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
682 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
683 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
684 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
685 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
a1820207
SC
686}
687
04cc6b77 688static void gfio_update_thread_status(char *status_message, double perc)
5b7573ab
SC
689{
690 static char message[100];
691 const char *m = message;
692
693 strncpy(message, status_message, sizeof(message) - 1);
04cc6b77
SC
694 gtk_progress_bar_set_text(
695 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
696 gtk_progress_bar_set_fraction(
697 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
5b7573ab
SC
698 gdk_threads_enter();
699 gtk_widget_queue_draw(ui.window);
700 gdk_threads_leave();
701}
702
3ec62ec4
JA
703static void gfio_quit_op(struct fio_client *client)
704{
705 struct gui *ui = client->client_data;
706
707 gfio_set_connected(ui, 0);
708}
709
807f9971
JA
710static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
711{
712 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
713 struct gui *ui = client->client_data;
714 char tmp[8];
715 int i;
716
717 p->iodepth = le32_to_cpu(p->iodepth);
718 p->rw = le32_to_cpu(p->rw);
719
720 for (i = 0; i < 2; i++) {
721 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
722 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
723 }
724
725 p->numjobs = le32_to_cpu(p->numjobs);
726 p->group_reporting = le32_to_cpu(p->group_reporting);
727
ca850992
JA
728 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
729 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
730 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
807f9971
JA
731
732 sprintf(tmp, "%u", p->iodepth);
ca850992 733 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
807f9971
JA
734}
735
ed727a46
JA
736static void gfio_client_timed_out(struct fio_client *client)
737{
738 struct gui *ui = client->client_data;
739 GtkWidget *dialog, *label, *content;
740 char buf[256];
741
742 gdk_threads_enter();
743
744 gfio_set_connected(ui, 0);
88432651 745 clear_ui_info(ui);
ed727a46
JA
746
747 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
748
749 dialog = gtk_dialog_new_with_buttons("Timed out!",
750 GTK_WINDOW(ui->window),
751 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
752 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
753
754 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
755 label = gtk_label_new((const gchar *) buf);
756 gtk_container_add(GTK_CONTAINER(content), label);
757 gtk_widget_show_all(dialog);
758 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
759
760 gtk_dialog_run(GTK_DIALOG(dialog));
761 gtk_widget_destroy(dialog);
762
763 gdk_threads_leave();
764}
765
a1820207 766struct client_ops gfio_client_ops = {
0420ba6a
JA
767 .text_op = gfio_text_op,
768 .disk_util = gfio_disk_util_op,
769 .thread_status = gfio_thread_status_op,
770 .group_stats = gfio_group_stats_op,
a5276616 771 .eta = gfio_update_eta,
0420ba6a 772 .probe = gfio_probe_op,
3ec62ec4 773 .quit = gfio_quit_op,
807f9971 774 .add_job = gfio_add_job_op,
ed727a46 775 .timed_out = gfio_client_timed_out,
3ec62ec4 776 .stay_connected = 1,
a1820207
SC
777};
778
ff1f3280
SC
779static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
780 __attribute__((unused)) gpointer data)
781{
782 gtk_main_quit();
783}
784
25927259
SC
785static void *job_thread(void *arg)
786{
25927259 787 fio_handle_clients(&gfio_client_ops);
25927259
SC
788 return NULL;
789}
790
0420ba6a 791static int send_job_files(struct gui *ui)
60f6b330 792{
441013b4 793 int i, ret = 0;
0420ba6a
JA
794
795 for (i = 0; i < ui->nr_job_files; i++) {
796 ret = fio_clients_send_ini(ui->job_files[i]);
441013b4
JA
797 if (ret)
798 break;
799
0420ba6a
JA
800 free(ui->job_files[i]);
801 ui->job_files[i] = NULL;
441013b4
JA
802 }
803 while (i < ui->nr_job_files) {
804 free(ui->job_files[i]);
805 ui->job_files[i] = NULL;
806 i++;
0420ba6a
JA
807 }
808
441013b4 809 return ret;
60f6b330
SC
810}
811
3ec62ec4 812static void start_job_thread(struct gui *ui)
25927259 813{
0420ba6a 814 if (send_job_files(ui)) {
60f6b330 815 printf("Yeah, I didn't really like those options too much.\n");
60f6b330
SC
816 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
817 return;
818 }
25927259
SC
819}
820
f3074008 821static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 822 gpointer data)
f3074008 823{
25927259
SC
824 struct gui *ui = data;
825
25927259 826 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
3ec62ec4 827 start_job_thread(ui);
f3074008
SC
828}
829
df06f220
JA
830static void file_open(GtkWidget *w, gpointer data);
831
832static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 833{
3ec62ec4
JA
834 struct gui *ui = data;
835
836 if (!ui->connected) {
df06f220
JA
837 if (!ui->nr_job_files)
838 file_open(widget, data);
8663ea65 839 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
3ec62ec4
JA
840 fio_clients_connect();
841 pthread_create(&ui->t, NULL, job_thread, NULL);
842 gfio_set_connected(ui, 1);
df06f220
JA
843 } else {
844 fio_clients_terminate();
3ec62ec4 845 gfio_set_connected(ui, 0);
88432651 846 clear_ui_info(ui);
df06f220 847 }
3e47bd25
JA
848}
849
f3074008
SC
850static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
851 struct button_spec *buttonspec)
852{
853 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
854 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
3ec62ec4 855 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
f3074008 856 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
3e47bd25 857 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
f3074008
SC
858}
859
860static void add_buttons(struct gui *ui,
861 struct button_spec *buttonlist,
862 int nbuttons)
863{
864 int i;
865
f3074008
SC
866 for (i = 0; i < nbuttons; i++)
867 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
868}
869
0420ba6a
JA
870static void on_info_bar_response(GtkWidget *widget, gint response,
871 gpointer data)
872{
873 if (response == GTK_RESPONSE_OK) {
874 gtk_widget_destroy(widget);
875 ui.error_info_bar = NULL;
876 }
877}
878
df06f220 879void report_error(GError *error)
0420ba6a
JA
880{
881 if (ui.error_info_bar == NULL) {
882 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
883 GTK_RESPONSE_OK,
884 NULL);
885 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
886 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
887 GTK_MESSAGE_ERROR);
888
889 ui.error_label = gtk_label_new(error->message);
890 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
891 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
892
893 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
894 gtk_widget_show_all(ui.vbox);
895 } else {
896 char buffer[256];
897 snprintf(buffer, sizeof(buffer), "Failed to open file.");
898 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
899 }
900}
901
b9f3c7ed
JA
902static int get_connection_details(char **host, int *port, int *type,
903 int *server_start)
a7a42ce1
JA
904{
905 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
b9f3c7ed 906 GtkWidget *button;
a7a42ce1
JA
907 char *typeentry;
908
909 dialog = gtk_dialog_new_with_buttons("Connection details",
910 GTK_WINDOW(ui.window),
911 GTK_DIALOG_DESTROY_WITH_PARENT,
912 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
913 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
914
915 frame = gtk_frame_new("Hostname / socket name");
916 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
917 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
918
919 box = gtk_vbox_new(FALSE, 6);
920 gtk_container_add(GTK_CONTAINER(frame), box);
921
922 hbox = gtk_hbox_new(TRUE, 10);
923 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
924 hentry = gtk_entry_new();
925 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
926 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
927
928 frame = gtk_frame_new("Port");
929 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
930 box = gtk_vbox_new(FALSE, 10);
931 gtk_container_add(GTK_CONTAINER(frame), box);
932
933 hbox = gtk_hbox_new(TRUE, 4);
934 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
935 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
936
937 frame = gtk_frame_new("Type");
938 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
939 box = gtk_vbox_new(FALSE, 10);
940 gtk_container_add(GTK_CONTAINER(frame), box);
941
942 hbox = gtk_hbox_new(TRUE, 4);
943 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
944
945 combo = gtk_combo_box_text_new();
946 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
947 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
948 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
949 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
950
951 gtk_container_add(GTK_CONTAINER(hbox), combo);
952
b9f3c7ed
JA
953 frame = gtk_frame_new("Options");
954 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
955 box = gtk_vbox_new(FALSE, 10);
956 gtk_container_add(GTK_CONTAINER(frame), box);
957
958 hbox = gtk_hbox_new(TRUE, 4);
959 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
960
961 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
962 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
963 gtk_widget_set_tooltip_text(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.");
964 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
965
a7a42ce1
JA
966 gtk_widget_show_all(dialog);
967
968 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
969 gtk_widget_destroy(dialog);
970 return 1;
971 }
972
973 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
974 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
975
976 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
977 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
978 *type = Fio_client_ipv4;
979 else if (!strncmp(typeentry, "IPv6", 4))
980 *type = Fio_client_ipv6;
981 else
982 *type = Fio_client_socket;
983 g_free(typeentry);
984
b9f3c7ed
JA
985 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
986
a7a42ce1
JA
987 gtk_widget_destroy(dialog);
988 return 0;
989}
990
0420ba6a
JA
991static void file_open(GtkWidget *w, gpointer data)
992{
993 GtkWidget *dialog;
994 GSList *filenames, *fn_glist;
995 GtkFileFilter *filter;
a7a42ce1 996 char *host;
b9f3c7ed 997 int port, type, server_start;
0420ba6a
JA
998
999 dialog = gtk_file_chooser_dialog_new("Open File",
1000 GTK_WINDOW(ui.window),
1001 GTK_FILE_CHOOSER_ACTION_OPEN,
1002 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1003 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1004 NULL);
1005 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1006
1007 filter = gtk_file_filter_new();
1008 gtk_file_filter_add_pattern(filter, "*.fio");
1009 gtk_file_filter_add_pattern(filter, "*.job");
1010 gtk_file_filter_add_mime_type(filter, "text/fio");
1011 gtk_file_filter_set_name(filter, "Fio job file");
1012 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1013
1014 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1015 gtk_widget_destroy(dialog);
1016 return;
1017 }
1018
1019 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1020
1021 gtk_widget_destroy(dialog);
1022
b9f3c7ed 1023 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1024 goto err;
1025
0420ba6a
JA
1026 filenames = fn_glist;
1027 while (filenames != NULL) {
0420ba6a
JA
1028 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1029 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1030 ui.nr_job_files++;
1031
a5276616 1032 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
df06f220
JA
1033 if (!ui.client) {
1034 GError *error;
1035
1036 error = g_error_new(g_quark_from_string("fio"), 1,
1037 "Failed to add client %s", host);
0420ba6a
JA
1038 report_error(error);
1039 g_error_free(error);
0420ba6a 1040 }
df06f220 1041 ui.client->client_data = &ui;
0420ba6a
JA
1042
1043 g_free(filenames->data);
1044 filenames = g_slist_next(filenames);
1045 }
a7a42ce1
JA
1046 free(host);
1047err:
0420ba6a 1048 g_slist_free(fn_glist);
0420ba6a
JA
1049}
1050
1051static void file_save(GtkWidget *w, gpointer data)
1052{
1053 GtkWidget *dialog;
1054
1055 dialog = gtk_file_chooser_dialog_new("Save File",
1056 GTK_WINDOW(ui.window),
1057 GTK_FILE_CHOOSER_ACTION_SAVE,
1058 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1059 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1060 NULL);
1061
1062 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1063 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1064
1065 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1066 char *filename;
1067
1068 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1069 // save_job_file(filename);
1070 g_free(filename);
1071 }
1072 gtk_widget_destroy(dialog);
1073}
1074
46974a7d
JA
1075static void preferences(GtkWidget *w, gpointer data)
1076{
1077 GtkWidget *dialog, *frame, *box, **buttons;
1078 int i;
1079
1080 dialog = gtk_dialog_new_with_buttons("Preferences",
1081 GTK_WINDOW(ui.window),
1082 GTK_DIALOG_DESTROY_WITH_PARENT,
1083 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1084 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1085 NULL);
1086
0b8d11ed 1087 frame = gtk_frame_new("Debug logging");
46974a7d
JA
1088 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1089 box = gtk_hbox_new(FALSE, 6);
1090 gtk_container_add(GTK_CONTAINER(frame), box);
1091
1092 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1093
1094 for (i = 0; i < FD_DEBUG_MAX; i++) {
1095 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 1096 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
1097 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1098 }
1099
1100 gtk_widget_show_all(dialog);
1101
1102 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1103 gtk_widget_destroy(dialog);
1104 return;
1105 }
1106
1107 for (i = 0; i < FD_DEBUG_MAX; i++) {
1108 int set;
1109
1110 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1111 if (set)
1112 fio_debug |= (1UL << i);
1113 }
1114
1115 gtk_widget_destroy(dialog);
1116}
1117
0420ba6a
JA
1118static void about_dialog(GtkWidget *w, gpointer data)
1119{
1120 gtk_show_about_dialog(NULL,
1121 "program-name", "gfio",
1122 "comments", "Gtk2 UI for fio",
1123 "license", "GPLv2",
1124 "version", fio_version_string,
1125 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1126 "logo-icon-name", "fio",
1127 /* Must be last: */
1128 NULL, NULL,
1129 NULL);
1130}
1131
1132static GtkActionEntry menu_items[] = {
46974a7d
JA
1133 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1134 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1135 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1136 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1137 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1138 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1139 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1140};
3e47bd25 1141static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
1142
1143static const gchar *ui_string = " \
1144 <ui> \
1145 <menubar name=\"MainMenu\"> \
1146 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1147 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1148 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1149 <separator name=\"Separator\"/> \
46974a7d
JA
1150 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1151 <separator name=\"Separator2\"/> \
0420ba6a
JA
1152 <menuitem name=\"Quit\" action=\"Quit\" /> \
1153 </menu> \
1154 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1155 <menuitem name=\"About\" action=\"About\" /> \
1156 </menu> \
1157 </menubar> \
1158 </ui> \
1159";
1160
1161static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1162{
1163 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1164 GError *error = 0;
1165
1166 action_group = gtk_action_group_new("Menu");
1167 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1168
1169 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1170 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1171
1172 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1173 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1174}
1175
1176void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1177 GtkWidget *vbox, GtkUIManager *ui_manager)
1178{
1179 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1180}
1181
ff1f3280
SC
1182static void init_ui(int *argc, char **argv[], struct gui *ui)
1183{
0420ba6a
JA
1184 GtkSettings *settings;
1185 GtkUIManager *uimanager;
843ad237 1186 GtkWidget *menu, *probe, *probe_frame, *probe_box;
0420ba6a
JA
1187
1188 memset(ui, 0, sizeof(*ui));
45032dd8 1189
2839f0c6 1190 /* Magical g*thread incantation, you just need this thread stuff.
04cc6b77 1191 * Without it, the update that happens in gfio_update_thread_status
2839f0c6
SC
1192 * doesn't really happen in a timely fashion, you need expose events
1193 */
ed727a46 1194 if (!g_thread_supported())
2839f0c6
SC
1195 g_thread_init(NULL);
1196 gdk_threads_init();
1197
ff1f3280 1198 gtk_init(argc, argv);
0420ba6a
JA
1199 settings = gtk_settings_get_default();
1200 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1201 g_type_init();
ff1f3280
SC
1202
1203 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1204 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1205 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1206
0420ba6a
JA
1207 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1208 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
ff1f3280 1209
5b7573ab
SC
1210 ui->vbox = gtk_vbox_new(FALSE, 0);
1211 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
04cc6b77 1212
0420ba6a
JA
1213 uimanager = gtk_ui_manager_new();
1214 menu = get_menubar_menu(ui->window, uimanager);
1215 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1216
c36f98d9
SC
1217 /*
1218 * Set up alignments for widgets at the top of ui,
1219 * align top left, expand horizontally but not vertically
1220 */
1221 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
3ec62ec4 1222 ui->topvbox = gtk_vbox_new(FALSE, 3);
c36f98d9 1223 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
e164534f 1224 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 1225
3e47bd25 1226 probe = gtk_frame_new("Job");
843ad237
JA
1227 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1228 probe_frame = gtk_vbox_new(FALSE, 3);
1229 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1230
1231 probe_box = gtk_hbox_new(FALSE, 3);
1232 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843ad237
JA
1233 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1234 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1235 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1236 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1237
3e47bd25
JA
1238 probe_box = gtk_hbox_new(FALSE, 3);
1239 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971 1240
ca850992
JA
1241 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1242 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1243 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1244 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1245 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1246 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3e47bd25
JA
1247
1248 probe_box = gtk_hbox_new(FALSE, 3);
1249 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
ca850992
JA
1250 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1251 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1252 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1253 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 1254
807f9971
JA
1255 /*
1256 * Only add this if we have a commit rate
1257 */
1258#if 0
3e47bd25
JA
1259 probe_box = gtk_hbox_new(FALSE, 3);
1260 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1261
1262 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1263 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1264
3e47bd25
JA
1265 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1266 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1267#endif
3e47bd25 1268
736f2dff
SC
1269 /*
1270 * Add a text box for text op messages
1271 */
1272 ui->textview = gtk_text_view_new();
1273 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1274 gtk_text_buffer_set_text(ui->text, "", -1);
1275 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1276 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1277 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1278 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1279 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1280 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
e164534f
SC
1281 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1282 TRUE, TRUE, 0);
736f2dff 1283
c36f98d9
SC
1284 /*
1285 * Set up alignments for widgets at the bottom of ui,
1286 * align bottom left, expand horizontally but not vertically
1287 */
1288 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1289 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1290 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
e164534f
SC
1291 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1292 FALSE, FALSE, 0);
c36f98d9 1293
f3074008 1294 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
3ec62ec4
JA
1295
1296 /*
1297 * Set up thread status progress bar
1298 */
1299 ui->thread_status_pb = gtk_progress_bar_new();
1300 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1301 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1302 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1303
1304
ff1f3280
SC
1305 gtk_widget_show_all(ui->window);
1306}
1307
8232e285 1308int main(int argc, char *argv[], char *envp[])
ff1f3280 1309{
8232e285
SC
1310 if (initialize_fio(envp))
1311 return 1;
0420ba6a
JA
1312 if (fio_init_options())
1313 return 1;
a1820207 1314
ff1f3280 1315 init_ui(&argc, &argv, &ui);
5b7573ab 1316
2839f0c6 1317 gdk_threads_enter();
ff1f3280 1318 gtk_main();
2839f0c6 1319 gdk_threads_leave();
ff1f3280
SC
1320 return 0;
1321}