Add completion percentiles to results output
[fio.git] / gfio.c
... / ...
CommitLineData
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 */
23#include <locale.h>
24#include <malloc.h>
25
26#include <glib.h>
27#include <gtk/gtk.h>
28
29#include "fio.h"
30
31static void gfio_update_thread_status(char *status_message, double perc);
32
33#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
37static void connect_clicked(GtkWidget *widget, gpointer data);
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;
44 const int start_insensitive;
45} buttonspeclist[] = {
46#define CONNECT_BUTTON 0
47#define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
49 { "Start Job",
50 start_job_clicked,
51 "Send current fio job to fio server to be executed", 1 },
52};
53
54struct probe_widget {
55 GtkWidget *hostname;
56 GtkWidget *os;
57 GtkWidget *arch;
58 GtkWidget *fio_ver;
59};
60
61struct eta_widget {
62 GtkWidget *name;
63 GtkWidget *iotype;
64 GtkWidget *ioengine;
65 GtkWidget *iodepth;
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
78struct gui {
79 GtkWidget *window;
80 GtkWidget *vbox;
81 GtkWidget *topvbox;
82 GtkWidget *topalign;
83 GtkWidget *bottomalign;
84 GtkWidget *thread_status_pb;
85 GtkWidget *buttonbox;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87 GtkWidget *scrolled_window;
88 GtkWidget *textview;
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
91 GtkTextBuffer *text;
92 struct probe_widget probe;
93 struct eta_widget eta;
94 int connected;
95 pthread_t t;
96
97 struct fio_client *client;
98 int nr_job_files;
99 char **job_files;
100} ui;
101
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
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();
126 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
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
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
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
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
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)";
321 GtkWidget *hbox, *label, *frame;
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
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
335 hbox = gtk_hbox_new(FALSE, 3);
336 gtk_container_add(GTK_CONTAINER(frame), hbox);
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);
411 if (ts->clat_percentiles)
412 gfio_show_clat_percentiles(vbox, ts, ddir);
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
468static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
469{
470#if 0
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);
481#else
482 fio_client_ops.text_op(client, cmd);
483#endif
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
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
498static void gfio_thread_status_op(struct fio_client *client,
499 struct fio_net_cmd *cmd)
500{
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 }
518}
519
520static void gfio_group_stats_op(struct fio_client *client,
521 struct fio_net_cmd *cmd)
522{
523 printf("gfio_group_stats_op called\n");
524 fio_client_ops.group_stats(client, cmd);
525}
526
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);
562
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
607static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
608{
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);
629}
630
631static void gfio_update_thread_status(char *status_message, double perc)
632{
633 static char message[100];
634 const char *m = message;
635
636 strncpy(message, status_message, sizeof(message) - 1);
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);
641 gdk_threads_enter();
642 gtk_widget_queue_draw(ui.window);
643 gdk_threads_leave();
644}
645
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
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
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);
688 clear_ui_info(ui);
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
709struct client_ops gfio_client_ops = {
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,
714 .eta = gfio_update_eta,
715 .probe = gfio_probe_op,
716 .quit = gfio_quit_op,
717 .add_job = gfio_add_job_op,
718 .timed_out = gfio_client_timed_out,
719 .stay_connected = 1,
720};
721
722static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
723 __attribute__((unused)) gpointer data)
724{
725 gtk_main_quit();
726}
727
728static void *job_thread(void *arg)
729{
730 fio_handle_clients(&gfio_client_ops);
731 return NULL;
732}
733
734static int send_job_files(struct gui *ui)
735{
736 int i, ret = 0;
737
738 for (i = 0; i < ui->nr_job_files; i++) {
739 ret = fio_clients_send_ini(ui->job_files[i]);
740 if (ret)
741 break;
742
743 free(ui->job_files[i]);
744 ui->job_files[i] = NULL;
745 }
746 while (i < ui->nr_job_files) {
747 free(ui->job_files[i]);
748 ui->job_files[i] = NULL;
749 i++;
750 }
751
752 return ret;
753}
754
755static void start_job_thread(struct gui *ui)
756{
757 if (send_job_files(ui)) {
758 printf("Yeah, I didn't really like those options too much.\n");
759 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
760 return;
761 }
762}
763
764static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
765 gpointer data)
766{
767 struct gui *ui = data;
768
769 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
770 start_job_thread(ui);
771}
772
773static void file_open(GtkWidget *w, gpointer data);
774
775static void connect_clicked(GtkWidget *widget, gpointer data)
776{
777 struct gui *ui = data;
778
779 if (!ui->connected) {
780 if (!ui->nr_job_files)
781 file_open(widget, data);
782 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
783 fio_clients_connect();
784 pthread_create(&ui->t, NULL, job_thread, NULL);
785 gfio_set_connected(ui, 1);
786 } else {
787 fio_clients_terminate();
788 gfio_set_connected(ui, 0);
789 clear_ui_info(ui);
790 }
791}
792
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);
798 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
799 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
800 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
801}
802
803static void add_buttons(struct gui *ui,
804 struct button_spec *buttonlist,
805 int nbuttons)
806{
807 int i;
808
809 for (i = 0; i < nbuttons; i++)
810 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
811}
812
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
822void report_error(GError *error)
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
845static int get_connection_details(char **host, int *port, int *type,
846 int *server_start)
847{
848 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
849 GtkWidget *button;
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
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
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
928 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
929
930 gtk_widget_destroy(dialog);
931 return 0;
932}
933
934static void file_open(GtkWidget *w, gpointer data)
935{
936 GtkWidget *dialog;
937 GSList *filenames, *fn_glist;
938 GtkFileFilter *filter;
939 char *host;
940 int port, type, server_start;
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));
963
964 gtk_widget_destroy(dialog);
965
966 if (get_connection_details(&host, &port, &type, &server_start))
967 goto err;
968
969 filenames = fn_glist;
970 while (filenames != NULL) {
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
975 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
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);
981 report_error(error);
982 g_error_free(error);
983 }
984 ui.client->client_data = &ui;
985
986 g_free(filenames->data);
987 filenames = g_slist_next(filenames);
988 }
989 free(host);
990err:
991 g_slist_free(fn_glist);
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
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
1030 frame = gtk_frame_new("Debug logging");
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);
1039 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
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
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[] = {
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) },
1083};
1084static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
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\"/> \
1093 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1094 <separator name=\"Separator2\"/> \
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
1125static void init_ui(int *argc, char **argv[], struct gui *ui)
1126{
1127 GtkSettings *settings;
1128 GtkUIManager *uimanager;
1129 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1130
1131 memset(ui, 0, sizeof(*ui));
1132
1133 /* Magical g*thread incantation, you just need this thread stuff.
1134 * Without it, the update that happens in gfio_update_thread_status
1135 * doesn't really happen in a timely fashion, you need expose events
1136 */
1137 if (!g_thread_supported())
1138 g_thread_init(NULL);
1139 gdk_threads_init();
1140
1141 gtk_init(argc, argv);
1142 settings = gtk_settings_get_default();
1143 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1144 g_type_init();
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
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);
1152
1153 ui->vbox = gtk_vbox_new(FALSE, 0);
1154 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1155
1156 uimanager = gtk_ui_manager_new();
1157 menu = get_menubar_menu(ui->window, uimanager);
1158 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1159
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);
1165 ui->topvbox = gtk_vbox_new(FALSE, 3);
1166 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1167 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1168
1169 probe = gtk_frame_new("Job");
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);
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
1181 probe_box = gtk_hbox_new(FALSE, 3);
1182 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
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");
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");
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");
1197
1198 /*
1199 * Only add this if we have a commit rate
1200 */
1201#if 0
1202 probe_box = gtk_hbox_new(FALSE, 3);
1203 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
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
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");
1210#endif
1211
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);
1224 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1225 TRUE, TRUE, 0);
1226
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);
1234 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1235 FALSE, FALSE, 0);
1236
1237 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
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);
1244 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1245 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1246
1247
1248 gtk_widget_show_all(ui->window);
1249}
1250
1251int main(int argc, char *argv[], char *envp[])
1252{
1253 if (initialize_fio(envp))
1254 return 1;
1255 if (fio_init_options())
1256 return 1;
1257
1258 init_ui(&argc, &argv, &ui);
1259
1260 gdk_threads_enter();
1261 gtk_main();
1262 gdk_threads_leave();
1263 return 0;
1264}