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