Move stat arrays into show/calc func
[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" };
0b761306 362 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
3650a3ca
JA
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
0b761306
JA
389 main_vbox = gtk_vbox_new(FALSE, 3);
390 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
3650a3ca
JA
391
392 box = gtk_hbox_new(FALSE, 3);
0b761306 393 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
3650a3ca
JA
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");
0b761306 413 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
ca850992
JA
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 426 if (ts->clat_percentiles)
0b761306 427 gfio_show_clat_percentiles(main_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
0b761306
JA
448 sprintf(tmp, "Bandwidth (%s)", bw_str);
449 frame = gtk_frame_new(tmp);
450 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
ca850992 451
0b761306
JA
452 box = gtk_hbox_new(FALSE, 3);
453 gtk_container_add(GTK_CONTAINER(frame), box);
ca850992 454
0b761306 455 label = new_info_label_in_frame(box, "Minimum");
ca850992 456 label_set_int_value(label, min);
0b761306 457 label = new_info_label_in_frame(box, "Maximum");
ca850992 458 label_set_int_value(label, max);
0b761306 459 label = new_info_label_in_frame(box, "Percentage of jobs");
ca850992
JA
460 sprintf(tmp, "%3.2f%%", p_of_agg);
461 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 462 label = new_info_label_in_frame(box, "Average");
ca850992
JA
463 sprintf(tmp, "%5.02f", mean);
464 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 465 label = new_info_label_in_frame(box, "Standard deviation");
ca850992
JA
466 sprintf(tmp, "%5.02f", dev);
467 gtk_label_set_text(GTK_LABEL(label), tmp);
468 }
469
3650a3ca
JA
470 free(io_p);
471 free(bw_p);
472 free(iops_p);
473}
474
475static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
476 struct group_run_stats *rs)
477{
478 GtkWidget *dialog, *box, *vbox, *entry, *content;
479 struct gui *ui = client->client_data;
480
481 gdk_threads_enter();
482
483 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
484 GTK_DIALOG_DESTROY_WITH_PARENT,
485 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
486
487 g_signal_connect_swapped(dialog, "response",
488 G_CALLBACK(gtk_widget_destroy),
489 dialog);
490
491 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
492
493 vbox = gtk_vbox_new(FALSE, 3);
494 gtk_container_add(GTK_CONTAINER(content), vbox);
495
496 box = gtk_hbox_new(TRUE, 3);
497 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
498
499 entry = new_info_entry_in_frame(box, "Name");
500 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
501 if (strlen(ts->description)) {
502 entry = new_info_entry_in_frame(box, "Description");
503 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
504 }
505 entry = new_info_entry_in_frame(box, "Group ID");
506 entry_set_int_value(entry, ts->groupid);
507 entry = new_info_entry_in_frame(box, "Jobs");
508 entry_set_int_value(entry, ts->members);
509 entry = new_info_entry_in_frame(box, "Error");
510 entry_set_int_value(entry, ts->error);
511 entry = new_info_entry_in_frame(box, "PID");
512 entry_set_int_value(entry, ts->pid);
513
514 if (ts->io_bytes[DDIR_READ])
515 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
516 if (ts->io_bytes[DDIR_WRITE])
517 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
518
519 gtk_widget_show_all(dialog);
520
521 gdk_threads_leave();
522}
523
084d1c6f 524static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 525{
807f9971 526#if 0
736f2dff
SC
527 GtkTextBuffer *buffer;
528 GtkTextIter end;
529
530 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
531 gdk_threads_enter();
532 gtk_text_buffer_get_end_iter(buffer, &end);
533 gtk_text_buffer_insert(buffer, &end, buf, -1);
534 gdk_threads_leave();
535 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
536 &end, 0.0, FALSE, 0.0,0.0);
807f9971 537#else
084d1c6f 538 fio_client_ops.text_op(client, cmd);
807f9971 539#endif
a1820207
SC
540}
541
542static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
543{
544 printf("gfio_disk_util_op called\n");
545 fio_client_ops.disk_util(client, cmd);
546}
547
3650a3ca
JA
548extern int sum_stat_clients;
549extern struct thread_stat client_ts;
550extern struct group_run_stats client_gs;
551
552static int sum_stat_nr;
553
89e5fad9
JA
554static void gfio_thread_status_op(struct fio_client *client,
555 struct fio_net_cmd *cmd)
a1820207 556{
3650a3ca
JA
557 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
558
559 gfio_display_ts(client, &p->ts, &p->rs);
560
561 if (sum_stat_clients == 1)
562 return;
563
564 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
565 sum_group_stats(&client_gs, &p->rs);
566
567 client_ts.members++;
568 client_ts.groupid = p->ts.groupid;
569
570 if (++sum_stat_nr == sum_stat_clients) {
571 strcpy(client_ts.name, "All clients");
572 gfio_display_ts(client, &client_ts, &client_gs);
573 }
a1820207
SC
574}
575
89e5fad9
JA
576static void gfio_group_stats_op(struct fio_client *client,
577 struct fio_net_cmd *cmd)
a1820207
SC
578{
579 printf("gfio_group_stats_op called\n");
89e5fad9 580 fio_client_ops.group_stats(client, cmd);
a1820207
SC
581}
582
3e47bd25
JA
583static void gfio_update_eta(struct jobs_eta *je)
584{
585 static int eta_good;
586 char eta_str[128];
587 char output[256];
588 char tmp[32];
589 double perc = 0.0;
590 int i2p = 0;
591
592 eta_str[0] = '\0';
593 output[0] = '\0';
594
595 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
596 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
597 eta_to_str(eta_str, je->eta_sec);
598 }
599
600 sprintf(tmp, "%u", je->nr_running);
ca850992 601 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
3e47bd25 602 sprintf(tmp, "%u", je->files_open);
ca850992 603 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
3e47bd25
JA
604
605#if 0
606 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
607 if (je->m_rate || je->t_rate) {
608 char *tr, *mr;
609
610 mr = num2str(je->m_rate, 4, 0, i2p);
611 tr = num2str(je->t_rate, 4, 0, i2p);
ca850992 612 gtk_entry_set_text(GTK_ENTRY(ui.eta);
3e47bd25
JA
613 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
614 free(tr);
615 free(mr);
616 } else if (je->m_iops || je->t_iops)
617 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 618
ca850992
JA
619 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
620 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
621 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
622 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
3e47bd25
JA
623#endif
624
625 if (je->eta_sec != INT_MAX && je->nr_running) {
626 char *iops_str[2];
627 char *rate_str[2];
628
629 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
630 strcpy(output, "-.-% done");
631 else {
632 eta_good = 1;
633 perc *= 100.0;
634 sprintf(output, "%3.1f%% done", perc);
635 }
636
637 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
638 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
639
640 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
641 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
642
ca850992
JA
643 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
644 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
645 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
646 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
3e47bd25
JA
647
648 free(rate_str[0]);
649 free(rate_str[1]);
650 free(iops_str[0]);
651 free(iops_str[1]);
652 }
653
654 if (eta_str[0]) {
655 char *dst = output + strlen(output);
656
657 sprintf(dst, " - %s", eta_str);
658 }
659
660 gfio_update_thread_status(output, perc);
661}
662
a1820207
SC
663static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
664{
843ad237
JA
665 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
666 const char *os, *arch;
667 char buf[64];
668
669 os = fio_get_os_string(probe->os);
670 if (!os)
671 os = "unknown";
672
673 arch = fio_get_arch_string(probe->arch);
674 if (!arch)
675 os = "unknown";
676
677 if (!client->name)
678 client->name = strdup((char *) probe->hostname);
679
680 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
681 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
682 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
683 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
684 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
a1820207
SC
685}
686
04cc6b77 687static void gfio_update_thread_status(char *status_message, double perc)
5b7573ab
SC
688{
689 static char message[100];
690 const char *m = message;
691
692 strncpy(message, status_message, sizeof(message) - 1);
04cc6b77
SC
693 gtk_progress_bar_set_text(
694 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
695 gtk_progress_bar_set_fraction(
696 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
5b7573ab
SC
697 gdk_threads_enter();
698 gtk_widget_queue_draw(ui.window);
699 gdk_threads_leave();
700}
701
3ec62ec4
JA
702static void gfio_quit_op(struct fio_client *client)
703{
704 struct gui *ui = client->client_data;
705
706 gfio_set_connected(ui, 0);
707}
708
807f9971
JA
709static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
710{
711 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
712 struct gui *ui = client->client_data;
713 char tmp[8];
714 int i;
715
716 p->iodepth = le32_to_cpu(p->iodepth);
717 p->rw = le32_to_cpu(p->rw);
718
719 for (i = 0; i < 2; i++) {
720 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
721 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
722 }
723
724 p->numjobs = le32_to_cpu(p->numjobs);
725 p->group_reporting = le32_to_cpu(p->group_reporting);
726
ca850992
JA
727 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
728 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
729 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
807f9971
JA
730
731 sprintf(tmp, "%u", p->iodepth);
ca850992 732 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
807f9971
JA
733}
734
ed727a46
JA
735static void gfio_client_timed_out(struct fio_client *client)
736{
737 struct gui *ui = client->client_data;
738 GtkWidget *dialog, *label, *content;
739 char buf[256];
740
741 gdk_threads_enter();
742
743 gfio_set_connected(ui, 0);
88432651 744 clear_ui_info(ui);
ed727a46
JA
745
746 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
747
748 dialog = gtk_dialog_new_with_buttons("Timed out!",
749 GTK_WINDOW(ui->window),
750 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
751 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
752
753 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
754 label = gtk_label_new((const gchar *) buf);
755 gtk_container_add(GTK_CONTAINER(content), label);
756 gtk_widget_show_all(dialog);
757 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
758
759 gtk_dialog_run(GTK_DIALOG(dialog));
760 gtk_widget_destroy(dialog);
761
762 gdk_threads_leave();
763}
764
a1820207 765struct client_ops gfio_client_ops = {
0420ba6a
JA
766 .text_op = gfio_text_op,
767 .disk_util = gfio_disk_util_op,
768 .thread_status = gfio_thread_status_op,
769 .group_stats = gfio_group_stats_op,
a5276616 770 .eta = gfio_update_eta,
0420ba6a 771 .probe = gfio_probe_op,
3ec62ec4 772 .quit = gfio_quit_op,
807f9971 773 .add_job = gfio_add_job_op,
ed727a46 774 .timed_out = gfio_client_timed_out,
3ec62ec4 775 .stay_connected = 1,
a1820207
SC
776};
777
ff1f3280
SC
778static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
779 __attribute__((unused)) gpointer data)
780{
781 gtk_main_quit();
782}
783
25927259
SC
784static void *job_thread(void *arg)
785{
25927259 786 fio_handle_clients(&gfio_client_ops);
25927259
SC
787 return NULL;
788}
789
0420ba6a 790static int send_job_files(struct gui *ui)
60f6b330 791{
441013b4 792 int i, ret = 0;
0420ba6a
JA
793
794 for (i = 0; i < ui->nr_job_files; i++) {
795 ret = fio_clients_send_ini(ui->job_files[i]);
441013b4
JA
796 if (ret)
797 break;
798
0420ba6a
JA
799 free(ui->job_files[i]);
800 ui->job_files[i] = NULL;
441013b4
JA
801 }
802 while (i < ui->nr_job_files) {
803 free(ui->job_files[i]);
804 ui->job_files[i] = NULL;
805 i++;
0420ba6a
JA
806 }
807
441013b4 808 return ret;
60f6b330
SC
809}
810
3ec62ec4 811static void start_job_thread(struct gui *ui)
25927259 812{
0420ba6a 813 if (send_job_files(ui)) {
60f6b330 814 printf("Yeah, I didn't really like those options too much.\n");
60f6b330
SC
815 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
816 return;
817 }
25927259
SC
818}
819
f3074008 820static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 821 gpointer data)
f3074008 822{
25927259
SC
823 struct gui *ui = data;
824
25927259 825 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
3ec62ec4 826 start_job_thread(ui);
f3074008
SC
827}
828
df06f220
JA
829static void file_open(GtkWidget *w, gpointer data);
830
831static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 832{
3ec62ec4
JA
833 struct gui *ui = data;
834
835 if (!ui->connected) {
df06f220
JA
836 if (!ui->nr_job_files)
837 file_open(widget, data);
8663ea65 838 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
3ec62ec4
JA
839 fio_clients_connect();
840 pthread_create(&ui->t, NULL, job_thread, NULL);
841 gfio_set_connected(ui, 1);
df06f220
JA
842 } else {
843 fio_clients_terminate();
3ec62ec4 844 gfio_set_connected(ui, 0);
88432651 845 clear_ui_info(ui);
df06f220 846 }
3e47bd25
JA
847}
848
f3074008
SC
849static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
850 struct button_spec *buttonspec)
851{
852 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
853 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
3ec62ec4 854 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
f3074008 855 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
3e47bd25 856 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
f3074008
SC
857}
858
859static void add_buttons(struct gui *ui,
860 struct button_spec *buttonlist,
861 int nbuttons)
862{
863 int i;
864
f3074008
SC
865 for (i = 0; i < nbuttons; i++)
866 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
867}
868
0420ba6a
JA
869static void on_info_bar_response(GtkWidget *widget, gint response,
870 gpointer data)
871{
872 if (response == GTK_RESPONSE_OK) {
873 gtk_widget_destroy(widget);
874 ui.error_info_bar = NULL;
875 }
876}
877
df06f220 878void report_error(GError *error)
0420ba6a
JA
879{
880 if (ui.error_info_bar == NULL) {
881 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
882 GTK_RESPONSE_OK,
883 NULL);
884 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
885 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
886 GTK_MESSAGE_ERROR);
887
888 ui.error_label = gtk_label_new(error->message);
889 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
890 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
891
892 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
893 gtk_widget_show_all(ui.vbox);
894 } else {
895 char buffer[256];
896 snprintf(buffer, sizeof(buffer), "Failed to open file.");
897 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
898 }
899}
900
b9f3c7ed
JA
901static int get_connection_details(char **host, int *port, int *type,
902 int *server_start)
a7a42ce1
JA
903{
904 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
b9f3c7ed 905 GtkWidget *button;
a7a42ce1
JA
906 char *typeentry;
907
908 dialog = gtk_dialog_new_with_buttons("Connection details",
909 GTK_WINDOW(ui.window),
910 GTK_DIALOG_DESTROY_WITH_PARENT,
911 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
912 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
913
914 frame = gtk_frame_new("Hostname / socket name");
915 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
916 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
917
918 box = gtk_vbox_new(FALSE, 6);
919 gtk_container_add(GTK_CONTAINER(frame), box);
920
921 hbox = gtk_hbox_new(TRUE, 10);
922 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
923 hentry = gtk_entry_new();
924 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
925 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
926
927 frame = gtk_frame_new("Port");
928 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
929 box = gtk_vbox_new(FALSE, 10);
930 gtk_container_add(GTK_CONTAINER(frame), box);
931
932 hbox = gtk_hbox_new(TRUE, 4);
933 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
934 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
935
936 frame = gtk_frame_new("Type");
937 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
938 box = gtk_vbox_new(FALSE, 10);
939 gtk_container_add(GTK_CONTAINER(frame), box);
940
941 hbox = gtk_hbox_new(TRUE, 4);
942 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
943
944 combo = gtk_combo_box_text_new();
945 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
946 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
947 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
948 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
949
950 gtk_container_add(GTK_CONTAINER(hbox), combo);
951
b9f3c7ed
JA
952 frame = gtk_frame_new("Options");
953 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
954 box = gtk_vbox_new(FALSE, 10);
955 gtk_container_add(GTK_CONTAINER(frame), box);
956
957 hbox = gtk_hbox_new(TRUE, 4);
958 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
959
960 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
961 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
962 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.");
963 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
964
a7a42ce1
JA
965 gtk_widget_show_all(dialog);
966
967 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
968 gtk_widget_destroy(dialog);
969 return 1;
970 }
971
972 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
973 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
974
975 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
976 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
977 *type = Fio_client_ipv4;
978 else if (!strncmp(typeentry, "IPv6", 4))
979 *type = Fio_client_ipv6;
980 else
981 *type = Fio_client_socket;
982 g_free(typeentry);
983
b9f3c7ed
JA
984 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
985
a7a42ce1
JA
986 gtk_widget_destroy(dialog);
987 return 0;
988}
989
0420ba6a
JA
990static void file_open(GtkWidget *w, gpointer data)
991{
992 GtkWidget *dialog;
993 GSList *filenames, *fn_glist;
994 GtkFileFilter *filter;
a7a42ce1 995 char *host;
b9f3c7ed 996 int port, type, server_start;
0420ba6a
JA
997
998 dialog = gtk_file_chooser_dialog_new("Open File",
999 GTK_WINDOW(ui.window),
1000 GTK_FILE_CHOOSER_ACTION_OPEN,
1001 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1002 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1003 NULL);
1004 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1005
1006 filter = gtk_file_filter_new();
1007 gtk_file_filter_add_pattern(filter, "*.fio");
1008 gtk_file_filter_add_pattern(filter, "*.job");
1009 gtk_file_filter_add_mime_type(filter, "text/fio");
1010 gtk_file_filter_set_name(filter, "Fio job file");
1011 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1012
1013 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1014 gtk_widget_destroy(dialog);
1015 return;
1016 }
1017
1018 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1019
1020 gtk_widget_destroy(dialog);
1021
b9f3c7ed 1022 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1023 goto err;
1024
0420ba6a
JA
1025 filenames = fn_glist;
1026 while (filenames != NULL) {
0420ba6a
JA
1027 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1028 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1029 ui.nr_job_files++;
1030
a5276616 1031 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
df06f220
JA
1032 if (!ui.client) {
1033 GError *error;
1034
1035 error = g_error_new(g_quark_from_string("fio"), 1,
1036 "Failed to add client %s", host);
0420ba6a
JA
1037 report_error(error);
1038 g_error_free(error);
0420ba6a 1039 }
df06f220 1040 ui.client->client_data = &ui;
0420ba6a
JA
1041
1042 g_free(filenames->data);
1043 filenames = g_slist_next(filenames);
1044 }
a7a42ce1
JA
1045 free(host);
1046err:
0420ba6a 1047 g_slist_free(fn_glist);
0420ba6a
JA
1048}
1049
1050static void file_save(GtkWidget *w, gpointer data)
1051{
1052 GtkWidget *dialog;
1053
1054 dialog = gtk_file_chooser_dialog_new("Save File",
1055 GTK_WINDOW(ui.window),
1056 GTK_FILE_CHOOSER_ACTION_SAVE,
1057 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1058 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1059 NULL);
1060
1061 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1062 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1063
1064 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1065 char *filename;
1066
1067 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1068 // save_job_file(filename);
1069 g_free(filename);
1070 }
1071 gtk_widget_destroy(dialog);
1072}
1073
46974a7d
JA
1074static void preferences(GtkWidget *w, gpointer data)
1075{
1076 GtkWidget *dialog, *frame, *box, **buttons;
1077 int i;
1078
1079 dialog = gtk_dialog_new_with_buttons("Preferences",
1080 GTK_WINDOW(ui.window),
1081 GTK_DIALOG_DESTROY_WITH_PARENT,
1082 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1083 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1084 NULL);
1085
0b8d11ed 1086 frame = gtk_frame_new("Debug logging");
46974a7d
JA
1087 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1088 box = gtk_hbox_new(FALSE, 6);
1089 gtk_container_add(GTK_CONTAINER(frame), box);
1090
1091 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1092
1093 for (i = 0; i < FD_DEBUG_MAX; i++) {
1094 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 1095 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
1096 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1097 }
1098
1099 gtk_widget_show_all(dialog);
1100
1101 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1102 gtk_widget_destroy(dialog);
1103 return;
1104 }
1105
1106 for (i = 0; i < FD_DEBUG_MAX; i++) {
1107 int set;
1108
1109 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1110 if (set)
1111 fio_debug |= (1UL << i);
1112 }
1113
1114 gtk_widget_destroy(dialog);
1115}
1116
0420ba6a
JA
1117static void about_dialog(GtkWidget *w, gpointer data)
1118{
1119 gtk_show_about_dialog(NULL,
1120 "program-name", "gfio",
1121 "comments", "Gtk2 UI for fio",
1122 "license", "GPLv2",
1123 "version", fio_version_string,
1124 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1125 "logo-icon-name", "fio",
1126 /* Must be last: */
1127 NULL, NULL,
1128 NULL);
1129}
1130
1131static GtkActionEntry menu_items[] = {
46974a7d
JA
1132 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1133 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1134 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1135 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1136 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1137 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1138 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1139};
3e47bd25 1140static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
1141
1142static const gchar *ui_string = " \
1143 <ui> \
1144 <menubar name=\"MainMenu\"> \
1145 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1146 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1147 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1148 <separator name=\"Separator\"/> \
46974a7d
JA
1149 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1150 <separator name=\"Separator2\"/> \
0420ba6a
JA
1151 <menuitem name=\"Quit\" action=\"Quit\" /> \
1152 </menu> \
1153 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1154 <menuitem name=\"About\" action=\"About\" /> \
1155 </menu> \
1156 </menubar> \
1157 </ui> \
1158";
1159
1160static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1161{
1162 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1163 GError *error = 0;
1164
1165 action_group = gtk_action_group_new("Menu");
1166 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1167
1168 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1169 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1170
1171 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1172 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1173}
1174
1175void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1176 GtkWidget *vbox, GtkUIManager *ui_manager)
1177{
1178 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1179}
1180
ff1f3280
SC
1181static void init_ui(int *argc, char **argv[], struct gui *ui)
1182{
0420ba6a
JA
1183 GtkSettings *settings;
1184 GtkUIManager *uimanager;
843ad237 1185 GtkWidget *menu, *probe, *probe_frame, *probe_box;
0420ba6a
JA
1186
1187 memset(ui, 0, sizeof(*ui));
45032dd8 1188
2839f0c6 1189 /* Magical g*thread incantation, you just need this thread stuff.
04cc6b77 1190 * Without it, the update that happens in gfio_update_thread_status
2839f0c6
SC
1191 * doesn't really happen in a timely fashion, you need expose events
1192 */
ed727a46 1193 if (!g_thread_supported())
2839f0c6
SC
1194 g_thread_init(NULL);
1195 gdk_threads_init();
1196
ff1f3280 1197 gtk_init(argc, argv);
0420ba6a
JA
1198 settings = gtk_settings_get_default();
1199 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1200 g_type_init();
ff1f3280
SC
1201
1202 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1203 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1204 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1205
0420ba6a
JA
1206 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1207 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
ff1f3280 1208
5b7573ab
SC
1209 ui->vbox = gtk_vbox_new(FALSE, 0);
1210 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
04cc6b77 1211
0420ba6a
JA
1212 uimanager = gtk_ui_manager_new();
1213 menu = get_menubar_menu(ui->window, uimanager);
1214 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1215
c36f98d9
SC
1216 /*
1217 * Set up alignments for widgets at the top of ui,
1218 * align top left, expand horizontally but not vertically
1219 */
1220 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
3ec62ec4 1221 ui->topvbox = gtk_vbox_new(FALSE, 3);
c36f98d9 1222 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
e164534f 1223 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 1224
3e47bd25 1225 probe = gtk_frame_new("Job");
843ad237
JA
1226 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1227 probe_frame = gtk_vbox_new(FALSE, 3);
1228 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1229
1230 probe_box = gtk_hbox_new(FALSE, 3);
1231 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843ad237
JA
1232 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1233 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1234 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1235 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1236
3e47bd25
JA
1237 probe_box = gtk_hbox_new(FALSE, 3);
1238 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971 1239
ca850992
JA
1240 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1241 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1242 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1243 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1244 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1245 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3e47bd25
JA
1246
1247 probe_box = gtk_hbox_new(FALSE, 3);
1248 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
ca850992
JA
1249 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1250 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1251 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1252 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 1253
807f9971
JA
1254 /*
1255 * Only add this if we have a commit rate
1256 */
1257#if 0
3e47bd25
JA
1258 probe_box = gtk_hbox_new(FALSE, 3);
1259 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1260
1261 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1262 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1263
3e47bd25
JA
1264 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1265 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1266#endif
3e47bd25 1267
736f2dff
SC
1268 /*
1269 * Add a text box for text op messages
1270 */
1271 ui->textview = gtk_text_view_new();
1272 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1273 gtk_text_buffer_set_text(ui->text, "", -1);
1274 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1275 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1276 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1277 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1278 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1279 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
e164534f
SC
1280 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1281 TRUE, TRUE, 0);
736f2dff 1282
c36f98d9
SC
1283 /*
1284 * Set up alignments for widgets at the bottom of ui,
1285 * align bottom left, expand horizontally but not vertically
1286 */
1287 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1288 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1289 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
e164534f
SC
1290 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1291 FALSE, FALSE, 0);
c36f98d9 1292
f3074008 1293 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
3ec62ec4
JA
1294
1295 /*
1296 * Set up thread status progress bar
1297 */
1298 ui->thread_status_pb = gtk_progress_bar_new();
1299 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1300 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1301 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1302
1303
ff1f3280
SC
1304 gtk_widget_show_all(ui->window);
1305}
1306
8232e285 1307int main(int argc, char *argv[], char *envp[])
ff1f3280 1308{
8232e285
SC
1309 if (initialize_fio(envp))
1310 return 1;
0420ba6a
JA
1311 if (fio_init_options())
1312 return 1;
a1820207 1313
ff1f3280 1314 init_ui(&argc, &argv, &ui);
5b7573ab 1315
2839f0c6 1316 gdk_threads_enter();
ff1f3280 1317 gtk_main();
2839f0c6 1318 gdk_threads_leave();
ff1f3280
SC
1319 return 0;
1320}