blob: d39aa2bf9ff10c59cb62c91d2f27fe17914cccbb [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
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 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010023#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010024#include <malloc.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010025
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010026#include <glib.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010027#include <gtk/gtk.h>
28
Stephen M. Cameron8232e282012-02-24 08:17:31 +010029#include "fio.h"
30
Jens Axboe3e47bd22012-02-29 13:45:02 +010031static void gfio_update_thread_status(char *status_message, double perc);
32
Stephen M. Cameronf3074002012-02-24 08:17:30 +010033#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
Jens Axboe3e47bd22012-02-29 13:45:02 +010037static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010038static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40static struct button_spec {
41 const char *buttontext;
42 clickfunction f;
43 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010044 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010045} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010046#define CONNECT_BUTTON 0
47#define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049 { "Start Job",
50 start_job_clicked,
Jens Axboe3e47bd22012-02-29 13:45:02 +010051 "Send current fio job to fio server to be executed", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010052};
53
Jens Axboe843ad232012-02-29 11:44:53 +010054struct probe_widget {
55 GtkWidget *hostname;
56 GtkWidget *os;
57 GtkWidget *arch;
58 GtkWidget *fio_ver;
59};
60
Jens Axboe3e47bd22012-02-29 13:45:02 +010061struct eta_widget {
Jens Axboe807f9972012-03-02 10:25:24 +010062 GtkWidget *name;
63 GtkWidget *iotype;
64 GtkWidget *ioengine;
65 GtkWidget *iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010066 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
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010078struct gui {
79 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010080 GtkWidget *vbox;
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +010081 GtkWidget *topvbox;
82 GtkWidget *topalign;
83 GtkWidget *bottomalign;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +010084 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010085 GtkWidget *buttonbox;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +010087 GtkWidget *scrolled_window;
88 GtkWidget *textview;
Jens Axboe0420ba62012-02-29 11:16:52 +010089 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
Stephen M. Cameron736f2df2012-02-24 08:17:32 +010091 GtkTextBuffer *text;
Jens Axboe843ad232012-02-29 11:44:53 +010092 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +010093 struct eta_widget eta;
Jens Axboe3ec62ec2012-03-01 12:01:29 +010094 int connected;
Stephen M. Cameron25927252012-02-24 08:17:31 +010095 pthread_t t;
Jens Axboe0420ba62012-02-29 11:16:52 +010096
Jens Axboe3ec62ec2012-03-01 12:01:29 +010097 struct fio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +010098 int nr_job_files;
99 char **job_files;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100100} ui;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100101
Jens Axboe8663ea62012-03-02 14:04:30 +0100102static 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), "");
Jens Axboeca850992012-03-05 20:04:43 +0100108 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), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100118}
119
Jens Axboe3650a3c2012-03-05 14:09:03 +0100120static 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();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100126 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100127 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
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100162static 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
Jens Axboe3650a3c2012-03-05 14:09:03 +0100175static 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
Jens Axboea2697902012-03-05 16:43:49 +0100191#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
Jens Axboe3650a3c2012-03-05 14:09:03 +0100317static 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)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100321 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100322 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
Jens Axboe3650a3c2012-03-05 14:09:03 +0100331 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
Jens Axboe3650a3c2012-03-05 14:09:03 +0100335 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100336 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100337
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
Jens Axboeca850992012-03-05 20:04:43 +0100354#define GFIO_CLAT 1
355#define GFIO_SLAT 2
356#define GFIO_LAT 4
357
Jens Axboe3650a3c2012-03-05 14:09:03 +0100358static 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" };
Jens Axboe0b761302012-03-05 20:44:11 +0100362 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100363 unsigned long min, max, runt;
364 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100365 unsigned int flags = 0;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100366 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
Jens Axboe0b761302012-03-05 20:44:11 +0100389 main_vbox = gtk_vbox_new(FALSE, 3);
390 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100391
392 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100393 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100394
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
Jens Axboeca850992012-03-05 20:04:43 +0100404 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
405 double p_of_agg = 100.0;
406 const char *bw_str = "KB";
407 char tmp[32];
408
409 if (rs->agg[ddir]) {
410 p_of_agg = mean * 100 / (double) rs->agg[ddir];
411 if (p_of_agg > 100.0)
412 p_of_agg = 100.0;
413 }
414
415 if (mean > 999999.9) {
416 min /= 1000.0;
417 max /= 1000.0;
418 mean /= 1000.0;
419 dev /= 1000.0;
420 bw_str = "MB";
421 }
422
Jens Axboe0b761302012-03-05 20:44:11 +0100423 sprintf(tmp, "Bandwidth (%s)", bw_str);
424 frame = gtk_frame_new(tmp);
425 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100426
Jens Axboe0b761302012-03-05 20:44:11 +0100427 box = gtk_hbox_new(FALSE, 3);
428 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100429
Jens Axboe0b761302012-03-05 20:44:11 +0100430 label = new_info_label_in_frame(box, "Minimum");
Jens Axboeca850992012-03-05 20:04:43 +0100431 label_set_int_value(label, min);
Jens Axboe0b761302012-03-05 20:44:11 +0100432 label = new_info_label_in_frame(box, "Maximum");
Jens Axboeca850992012-03-05 20:04:43 +0100433 label_set_int_value(label, max);
Jens Axboe0b761302012-03-05 20:44:11 +0100434 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100435 sprintf(tmp, "%3.2f%%", p_of_agg);
436 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100437 label = new_info_label_in_frame(box, "Average");
Jens Axboeca850992012-03-05 20:04:43 +0100438 sprintf(tmp, "%5.02f", mean);
439 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100440 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboeca850992012-03-05 20:04:43 +0100441 sprintf(tmp, "%5.02f", dev);
442 gtk_label_set_text(GTK_LABEL(label), tmp);
443 }
444
Jens Axboe2b089892012-03-06 08:09:17 +0100445 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
446 flags |= GFIO_SLAT;
447 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
448 flags |= GFIO_CLAT;
449 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
450 flags |= GFIO_LAT;
451
452 if (flags) {
453 frame = gtk_frame_new("Latency");
454 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
455
456 vbox = gtk_vbox_new(FALSE, 3);
457 gtk_container_add(GTK_CONTAINER(frame), vbox);
458
459 if (flags & GFIO_SLAT)
460 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
461 if (flags & GFIO_CLAT)
462 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
463 if (flags & GFIO_LAT)
464 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
465 }
466
467 if (ts->clat_percentiles)
468 gfio_show_clat_percentiles(main_vbox, ts, ddir);
469
470
Jens Axboe3650a3c2012-03-05 14:09:03 +0100471 free(io_p);
472 free(bw_p);
473 free(iops_p);
474}
475
Jens Axboee5bd1342012-03-05 21:38:12 +0100476static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
477 const char **labels)
478{
479 GtkWidget *tree_view;
480 GtkTreeSelection *selection;
481 GtkListStore *model;
482 GtkTreeIter iter;
483 GType *types;
484 int i, skipped;
485
486 /*
487 * Check if all are empty, in which case don't bother
488 */
489 for (i = 0, skipped = 0; i < num; i++)
490 if (lat[i] <= 0.0)
491 skipped++;
492
493 if (skipped == num)
494 return NULL;
495
496 types = malloc(num * sizeof(GType));
497
498 for (i = 0; i < num; i++)
499 types[i] = G_TYPE_STRING;
500
501 model = gtk_list_store_newv(num, types);
502 free(types);
503 types = NULL;
504
505 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
506 gtk_widget_set_can_focus(tree_view, FALSE);
507
508 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
509 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
510
511 for (i = 0; i < num; i++)
512 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
513
514 gtk_list_store_append(model, &iter);
515
516 for (i = 0; i < num; i++) {
517 char fbuf[32];
518
519 if (lat[i] <= 0.0)
520 sprintf(fbuf, "0.00");
521 else
522 sprintf(fbuf, "%3.2f%%", lat[i]);
523
524 gtk_list_store_set(model, &iter, i, fbuf, -1);
525 }
526
527 return tree_view;
528}
529
530static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
531{
532 GtkWidget *box, *frame, *tree_view;
533 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
534 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
535 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
536 "250", "500", "750", "1000", };
537 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
538 "250", "500", "750", "1000", "2000",
539 ">= 2000", };
540
541 stat_calc_lat_u(ts, io_u_lat_u);
542 stat_calc_lat_m(ts, io_u_lat_m);
543
544 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
545 if (tree_view) {
546 frame = gtk_frame_new("Latency buckets (usec)");
547 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
548
549 box = gtk_hbox_new(FALSE, 3);
550 gtk_container_add(GTK_CONTAINER(frame), box);
551 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
552 }
553
554 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
555 if (tree_view) {
556 frame = gtk_frame_new("Latency buckets (msec)");
557 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558
559 box = gtk_hbox_new(FALSE, 3);
560 gtk_container_add(GTK_CONTAINER(frame), box);
561 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
562 }
563}
564
Jens Axboe2e331012012-03-05 22:07:54 +0100565static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
566{
567 GtkWidget *box, *frame, *entry;
568 double usr_cpu, sys_cpu;
569 unsigned long runtime;
570 char tmp[32];
571
572 runtime = ts->total_run_time;
573 if (runtime) {
574 double runt = (double) runtime;
575
576 usr_cpu = (double) ts->usr_time * 100 / runt;
577 sys_cpu = (double) ts->sys_time * 100 / runt;
578 } else {
579 usr_cpu = 0;
580 sys_cpu = 0;
581 }
582
583 frame = gtk_frame_new("OS resources");
584 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585
586 box = gtk_hbox_new(FALSE, 3);
587 gtk_container_add(GTK_CONTAINER(frame), box);
588
589 entry = new_info_entry_in_frame(box, "User CPU");
590 sprintf(tmp, "%3.2f%%", usr_cpu);
591 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
592 entry = new_info_entry_in_frame(box, "System CPU");
593 sprintf(tmp, "%3.2f%%", sys_cpu);
594 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
595 entry = new_info_entry_in_frame(box, "Context switches");
596 entry_set_int_value(entry, ts->ctx);
597 entry = new_info_entry_in_frame(box, "Major faults");
598 entry_set_int_value(entry, ts->majf);
599 entry = new_info_entry_in_frame(box, "Minor faults");
600 entry_set_int_value(entry, ts->minf);
601}
Jens Axboe19998db2012-03-06 09:17:59 +0100602static void gfio_add_sc_depths_tree(GtkListStore *model,
603 struct thread_stat *ts, unsigned int len,
604 int submit)
605{
606 double io_u_dist[FIO_IO_U_MAP_NR];
607 GtkTreeIter iter;
608 /* Bits 0, and 3-8 */
609 const int add_mask = 0x1f9;
610 int i, j;
611
612 if (submit)
613 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
614 else
615 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
616
617 gtk_list_store_append(model, &iter);
618
619 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
620
621 for (i = 1, j = 0; i < len; i++) {
622 char fbuf[32];
623
624 if (!(add_mask & (1UL << (i - 1))))
625 sprintf(fbuf, "0.0%%");
626 else {
627 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
628 j++;
629 }
630
631 gtk_list_store_set(model, &iter, i, fbuf, -1);
632 }
633
634}
635
636static void gfio_add_total_depths_tree(GtkListStore *model,
637 struct thread_stat *ts, unsigned int len)
638{
639 double io_u_dist[FIO_IO_U_MAP_NR];
640 GtkTreeIter iter;
641 /* Bits 1-6, and 8 */
642 const int add_mask = 0x17e;
643 int i, j;
644
645 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
646
647 gtk_list_store_append(model, &iter);
648
649 gtk_list_store_set(model, &iter, 0, "Total", -1);
650
651 for (i = 1, j = 0; i < len; i++) {
652 char fbuf[32];
653
654 if (!(add_mask & (1UL << (i - 1))))
655 sprintf(fbuf, "0.0%%");
656 else {
657 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
658 j++;
659 }
660
661 gtk_list_store_set(model, &iter, i, fbuf, -1);
662 }
663
664}
Jens Axboe2e331012012-03-05 22:07:54 +0100665
666static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
667{
Jens Axboe2e331012012-03-05 22:07:54 +0100668 GtkWidget *frame, *box, *tree_view;
669 GtkTreeSelection *selection;
670 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100671 GType types[FIO_IO_U_MAP_NR + 1];
672 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100673#define NR_LABELS 10
674 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100675
676 frame = gtk_frame_new("IO depths");
677 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
678
679 box = gtk_hbox_new(FALSE, 3);
680 gtk_container_add(GTK_CONTAINER(frame), box);
681
Jens Axboe19998db2012-03-06 09:17:59 +0100682 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100683 types[i] = G_TYPE_STRING;
684
Jens Axboe19998db2012-03-06 09:17:59 +0100685 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100686
687 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
688 gtk_widget_set_can_focus(tree_view, FALSE);
689
690 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
691 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
692
Jens Axboe19998db2012-03-06 09:17:59 +0100693 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100694 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
695
Jens Axboe19998db2012-03-06 09:17:59 +0100696 gfio_add_total_depths_tree(model, ts, NR_LABELS);
697 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
698 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100699
700 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
701}
702
Jens Axboe3650a3c2012-03-05 14:09:03 +0100703static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
704 struct group_run_stats *rs)
705{
706 GtkWidget *dialog, *box, *vbox, *entry, *content;
707 struct gui *ui = client->client_data;
708
709 gdk_threads_enter();
710
711 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
712 GTK_DIALOG_DESTROY_WITH_PARENT,
713 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
714
715 g_signal_connect_swapped(dialog, "response",
716 G_CALLBACK(gtk_widget_destroy),
717 dialog);
718
719 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
720
721 vbox = gtk_vbox_new(FALSE, 3);
722 gtk_container_add(GTK_CONTAINER(content), vbox);
723
724 box = gtk_hbox_new(TRUE, 3);
725 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
726
727 entry = new_info_entry_in_frame(box, "Name");
728 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
729 if (strlen(ts->description)) {
730 entry = new_info_entry_in_frame(box, "Description");
731 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
732 }
733 entry = new_info_entry_in_frame(box, "Group ID");
734 entry_set_int_value(entry, ts->groupid);
735 entry = new_info_entry_in_frame(box, "Jobs");
736 entry_set_int_value(entry, ts->members);
737 entry = new_info_entry_in_frame(box, "Error");
738 entry_set_int_value(entry, ts->error);
739 entry = new_info_entry_in_frame(box, "PID");
740 entry_set_int_value(entry, ts->pid);
741
742 if (ts->io_bytes[DDIR_READ])
743 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
744 if (ts->io_bytes[DDIR_WRITE])
745 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
746
Jens Axboee5bd1342012-03-05 21:38:12 +0100747 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100748 gfio_show_cpu_usage(vbox, ts);
749 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100750
Jens Axboe3650a3c2012-03-05 14:09:03 +0100751 gtk_widget_show_all(dialog);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100752 gdk_threads_leave();
753}
754
Jens Axboe084d1c62012-03-03 20:28:07 +0100755static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100756{
Jens Axboe807f9972012-03-02 10:25:24 +0100757#if 0
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100758 GtkTextBuffer *buffer;
759 GtkTextIter end;
760
761 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
762 gdk_threads_enter();
763 gtk_text_buffer_get_end_iter(buffer, &end);
764 gtk_text_buffer_insert(buffer, &end, buf, -1);
765 gdk_threads_leave();
766 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
767 &end, 0.0, FALSE, 0.0,0.0);
Jens Axboe807f9972012-03-02 10:25:24 +0100768#else
Jens Axboe084d1c62012-03-03 20:28:07 +0100769 fio_client_ops.text_op(client, cmd);
Jens Axboe807f9972012-03-02 10:25:24 +0100770#endif
Stephen M. Camerona1820202012-02-24 08:17:31 +0100771}
772
773static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
774{
Jens Axboe0050e5f2012-03-06 09:23:27 +0100775 gdk_threads_enter();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100776 printf("gfio_disk_util_op called\n");
777 fio_client_ops.disk_util(client, cmd);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100778 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100779}
780
Jens Axboe3650a3c2012-03-05 14:09:03 +0100781extern int sum_stat_clients;
782extern struct thread_stat client_ts;
783extern struct group_run_stats client_gs;
784
785static int sum_stat_nr;
786
Jens Axboe89e5fad2012-03-05 09:21:12 +0100787static void gfio_thread_status_op(struct fio_client *client,
788 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100789{
Jens Axboe3650a3c2012-03-05 14:09:03 +0100790 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
791
792 gfio_display_ts(client, &p->ts, &p->rs);
793
794 if (sum_stat_clients == 1)
795 return;
796
797 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
798 sum_group_stats(&client_gs, &p->rs);
799
800 client_ts.members++;
801 client_ts.groupid = p->ts.groupid;
802
803 if (++sum_stat_nr == sum_stat_clients) {
804 strcpy(client_ts.name, "All clients");
805 gfio_display_ts(client, &client_ts, &client_gs);
806 }
Stephen M. Camerona1820202012-02-24 08:17:31 +0100807}
808
Jens Axboe89e5fad2012-03-05 09:21:12 +0100809static void gfio_group_stats_op(struct fio_client *client,
810 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100811{
Jens Axboe0050e5f2012-03-06 09:23:27 +0100812 gdk_threads_enter();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100813 printf("gfio_group_stats_op called\n");
Jens Axboe89e5fad2012-03-05 09:21:12 +0100814 fio_client_ops.group_stats(client, cmd);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100815 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100816}
817
Jens Axboe3e47bd22012-02-29 13:45:02 +0100818static void gfio_update_eta(struct jobs_eta *je)
819{
820 static int eta_good;
821 char eta_str[128];
822 char output[256];
823 char tmp[32];
824 double perc = 0.0;
825 int i2p = 0;
826
Jens Axboe0050e5f2012-03-06 09:23:27 +0100827 gdk_threads_enter();
828
Jens Axboe3e47bd22012-02-29 13:45:02 +0100829 eta_str[0] = '\0';
830 output[0] = '\0';
831
832 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
833 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
834 eta_to_str(eta_str, je->eta_sec);
835 }
836
837 sprintf(tmp, "%u", je->nr_running);
Jens Axboeca850992012-03-05 20:04:43 +0100838 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100839 sprintf(tmp, "%u", je->files_open);
Jens Axboeca850992012-03-05 20:04:43 +0100840 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100841
842#if 0
843 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
844 if (je->m_rate || je->t_rate) {
845 char *tr, *mr;
846
847 mr = num2str(je->m_rate, 4, 0, i2p);
848 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboeca850992012-03-05 20:04:43 +0100849 gtk_entry_set_text(GTK_ENTRY(ui.eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100850 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
851 free(tr);
852 free(mr);
853 } else if (je->m_iops || je->t_iops)
854 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +0100855
Jens Axboeca850992012-03-05 20:04:43 +0100856 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
857 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
858 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
859 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +0100860#endif
861
862 if (je->eta_sec != INT_MAX && je->nr_running) {
863 char *iops_str[2];
864 char *rate_str[2];
865
866 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
867 strcpy(output, "-.-% done");
868 else {
869 eta_good = 1;
870 perc *= 100.0;
871 sprintf(output, "%3.1f%% done", perc);
872 }
873
874 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
875 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
876
877 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
878 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
879
Jens Axboeca850992012-03-05 20:04:43 +0100880 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
881 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
882 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
883 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100884
885 free(rate_str[0]);
886 free(rate_str[1]);
887 free(iops_str[0]);
888 free(iops_str[1]);
889 }
890
891 if (eta_str[0]) {
892 char *dst = output + strlen(output);
893
894 sprintf(dst, " - %s", eta_str);
895 }
896
897 gfio_update_thread_status(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100898 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +0100899}
900
Stephen M. Camerona1820202012-02-24 08:17:31 +0100901static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
902{
Jens Axboe843ad232012-02-29 11:44:53 +0100903 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
904 const char *os, *arch;
905 char buf[64];
906
907 os = fio_get_os_string(probe->os);
908 if (!os)
909 os = "unknown";
910
911 arch = fio_get_arch_string(probe->arch);
912 if (!arch)
913 os = "unknown";
914
915 if (!client->name)
916 client->name = strdup((char *) probe->hostname);
917
Jens Axboe0050e5f2012-03-06 09:23:27 +0100918 gdk_threads_enter();
919
Jens Axboe843ad232012-02-29 11:44:53 +0100920 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
921 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
922 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
923 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
924 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100925
926 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100927}
928
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100929static void gfio_update_thread_status(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100930{
931 static char message[100];
932 const char *m = message;
933
934 strncpy(message, status_message, sizeof(message) - 1);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100935 gtk_progress_bar_set_text(
936 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
937 gtk_progress_bar_set_fraction(
938 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100939 gtk_widget_queue_draw(ui.window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100940}
941
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100942static void gfio_quit_op(struct fio_client *client)
943{
944 struct gui *ui = client->client_data;
945
Jens Axboe0050e5f2012-03-06 09:23:27 +0100946 gdk_threads_enter();
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100947 gfio_set_connected(ui, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100948 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100949}
950
Jens Axboe807f9972012-03-02 10:25:24 +0100951static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
952{
953 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
954 struct gui *ui = client->client_data;
955 char tmp[8];
956 int i;
957
958 p->iodepth = le32_to_cpu(p->iodepth);
959 p->rw = le32_to_cpu(p->rw);
960
961 for (i = 0; i < 2; i++) {
962 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
963 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
964 }
965
966 p->numjobs = le32_to_cpu(p->numjobs);
967 p->group_reporting = le32_to_cpu(p->group_reporting);
968
Jens Axboe0050e5f2012-03-06 09:23:27 +0100969 gdk_threads_enter();
970
Jens Axboeca850992012-03-05 20:04:43 +0100971 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
972 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
973 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +0100974
975 sprintf(tmp, "%u", p->iodepth);
Jens Axboeca850992012-03-05 20:04:43 +0100976 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100977
978 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +0100979}
980
Jens Axboeed727a42012-03-02 12:14:40 +0100981static void gfio_client_timed_out(struct fio_client *client)
982{
983 struct gui *ui = client->client_data;
984 GtkWidget *dialog, *label, *content;
985 char buf[256];
986
987 gdk_threads_enter();
988
989 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +0100990 clear_ui_info(ui);
Jens Axboeed727a42012-03-02 12:14:40 +0100991
992 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
993
994 dialog = gtk_dialog_new_with_buttons("Timed out!",
995 GTK_WINDOW(ui->window),
996 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
997 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
998
999 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1000 label = gtk_label_new((const gchar *) buf);
1001 gtk_container_add(GTK_CONTAINER(content), label);
1002 gtk_widget_show_all(dialog);
1003 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1004
1005 gtk_dialog_run(GTK_DIALOG(dialog));
1006 gtk_widget_destroy(dialog);
1007
1008 gdk_threads_leave();
1009}
1010
Stephen M. Camerona1820202012-02-24 08:17:31 +01001011struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001012 .text_op = gfio_text_op,
1013 .disk_util = gfio_disk_util_op,
1014 .thread_status = gfio_thread_status_op,
1015 .group_stats = gfio_group_stats_op,
Jens Axboea5276612012-03-04 15:15:08 +01001016 .eta = gfio_update_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001017 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001018 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001019 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001020 .timed_out = gfio_client_timed_out,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001021 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001022};
1023
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001024static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1025 __attribute__((unused)) gpointer data)
1026{
1027 gtk_main_quit();
1028}
1029
Stephen M. Cameron25927252012-02-24 08:17:31 +01001030static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001031{
Stephen M. Cameron25927252012-02-24 08:17:31 +01001032 fio_handle_clients(&gfio_client_ops);
Stephen M. Cameron25927252012-02-24 08:17:31 +01001033 return NULL;
1034}
1035
Jens Axboe0420ba62012-02-29 11:16:52 +01001036static int send_job_files(struct gui *ui)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001037{
Jens Axboe441013b2012-03-01 08:01:52 +01001038 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001039
Jens Axboe0420ba62012-02-29 11:16:52 +01001040 for (i = 0; i < ui->nr_job_files; i++) {
1041 ret = fio_clients_send_ini(ui->job_files[i]);
Jens Axboe441013b2012-03-01 08:01:52 +01001042 if (ret)
1043 break;
1044
Jens Axboe0420ba62012-02-29 11:16:52 +01001045 free(ui->job_files[i]);
1046 ui->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001047 }
1048 while (i < ui->nr_job_files) {
1049 free(ui->job_files[i]);
1050 ui->job_files[i] = NULL;
1051 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001052 }
1053
Jens Axboe441013b2012-03-01 08:01:52 +01001054 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001055}
1056
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001057static void start_job_thread(struct gui *ui)
Stephen M. Cameron25927252012-02-24 08:17:31 +01001058{
Jens Axboe0420ba62012-02-29 11:16:52 +01001059 if (send_job_files(ui)) {
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001060 printf("Yeah, I didn't really like those options too much.\n");
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001061 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1062 return;
1063 }
Stephen M. Cameron25927252012-02-24 08:17:31 +01001064}
1065
1066static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1067 gpointer data)
1068{
1069 struct gui *ui = data;
1070
Stephen M. Cameron25927252012-02-24 08:17:31 +01001071 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001072 start_job_thread(ui);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001073}
1074
Jens Axboedf06f222012-03-02 13:32:04 +01001075static void file_open(GtkWidget *w, gpointer data);
1076
1077static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001078{
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001079 struct gui *ui = data;
1080
1081 if (!ui->connected) {
Jens Axboedf06f222012-03-02 13:32:04 +01001082 if (!ui->nr_job_files)
1083 file_open(widget, data);
Jens Axboe8663ea62012-03-02 14:04:30 +01001084 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001085 fio_clients_connect();
1086 pthread_create(&ui->t, NULL, job_thread, NULL);
1087 gfio_set_connected(ui, 1);
Jens Axboedf06f222012-03-02 13:32:04 +01001088 } else {
1089 fio_clients_terminate();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001090 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +01001091 clear_ui_info(ui);
Jens Axboedf06f222012-03-02 13:32:04 +01001092 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001093}
1094
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001095static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1096 struct button_spec *buttonspec)
1097{
1098 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1099 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001100 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001101 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001102 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001103}
1104
1105static void add_buttons(struct gui *ui,
1106 struct button_spec *buttonlist,
1107 int nbuttons)
1108{
1109 int i;
1110
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001111 for (i = 0; i < nbuttons; i++)
1112 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1113}
1114
Jens Axboe0420ba62012-02-29 11:16:52 +01001115static void on_info_bar_response(GtkWidget *widget, gint response,
1116 gpointer data)
1117{
1118 if (response == GTK_RESPONSE_OK) {
1119 gtk_widget_destroy(widget);
1120 ui.error_info_bar = NULL;
1121 }
1122}
1123
Jens Axboedf06f222012-03-02 13:32:04 +01001124void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001125{
1126 if (ui.error_info_bar == NULL) {
1127 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1128 GTK_RESPONSE_OK,
1129 NULL);
1130 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1131 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1132 GTK_MESSAGE_ERROR);
1133
1134 ui.error_label = gtk_label_new(error->message);
1135 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1136 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1137
1138 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1139 gtk_widget_show_all(ui.vbox);
1140 } else {
1141 char buffer[256];
1142 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1143 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1144 }
1145}
1146
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001147static int get_connection_details(char **host, int *port, int *type,
1148 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001149{
1150 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001151 GtkWidget *button;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001152 char *typeentry;
1153
1154 dialog = gtk_dialog_new_with_buttons("Connection details",
1155 GTK_WINDOW(ui.window),
1156 GTK_DIALOG_DESTROY_WITH_PARENT,
1157 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1158 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1159
1160 frame = gtk_frame_new("Hostname / socket name");
1161 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1162 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1163
1164 box = gtk_vbox_new(FALSE, 6);
1165 gtk_container_add(GTK_CONTAINER(frame), box);
1166
1167 hbox = gtk_hbox_new(TRUE, 10);
1168 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1169 hentry = gtk_entry_new();
1170 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1171 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1172
1173 frame = gtk_frame_new("Port");
1174 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1175 box = gtk_vbox_new(FALSE, 10);
1176 gtk_container_add(GTK_CONTAINER(frame), box);
1177
1178 hbox = gtk_hbox_new(TRUE, 4);
1179 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1180 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1181
1182 frame = gtk_frame_new("Type");
1183 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1184 box = gtk_vbox_new(FALSE, 10);
1185 gtk_container_add(GTK_CONTAINER(frame), box);
1186
1187 hbox = gtk_hbox_new(TRUE, 4);
1188 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1189
1190 combo = gtk_combo_box_text_new();
1191 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1192 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1193 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1194 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1195
1196 gtk_container_add(GTK_CONTAINER(hbox), combo);
1197
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001198 frame = gtk_frame_new("Options");
1199 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1200 box = gtk_vbox_new(FALSE, 10);
1201 gtk_container_add(GTK_CONTAINER(frame), box);
1202
1203 hbox = gtk_hbox_new(TRUE, 4);
1204 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1205
1206 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1207 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1208 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.");
1209 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1210
Jens Axboea7a42ce2012-03-02 13:12:04 +01001211 gtk_widget_show_all(dialog);
1212
1213 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1214 gtk_widget_destroy(dialog);
1215 return 1;
1216 }
1217
1218 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1219 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1220
1221 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1222 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1223 *type = Fio_client_ipv4;
1224 else if (!strncmp(typeentry, "IPv6", 4))
1225 *type = Fio_client_ipv6;
1226 else
1227 *type = Fio_client_socket;
1228 g_free(typeentry);
1229
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001230 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1231
Jens Axboea7a42ce2012-03-02 13:12:04 +01001232 gtk_widget_destroy(dialog);
1233 return 0;
1234}
1235
Jens Axboe0420ba62012-02-29 11:16:52 +01001236static void file_open(GtkWidget *w, gpointer data)
1237{
1238 GtkWidget *dialog;
1239 GSList *filenames, *fn_glist;
1240 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001241 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001242 int port, type, server_start;
Jens Axboe0420ba62012-02-29 11:16:52 +01001243
1244 dialog = gtk_file_chooser_dialog_new("Open File",
1245 GTK_WINDOW(ui.window),
1246 GTK_FILE_CHOOSER_ACTION_OPEN,
1247 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1248 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1249 NULL);
1250 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1251
1252 filter = gtk_file_filter_new();
1253 gtk_file_filter_add_pattern(filter, "*.fio");
1254 gtk_file_filter_add_pattern(filter, "*.job");
1255 gtk_file_filter_add_mime_type(filter, "text/fio");
1256 gtk_file_filter_set_name(filter, "Fio job file");
1257 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1258
1259 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1260 gtk_widget_destroy(dialog);
1261 return;
1262 }
1263
1264 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001265
1266 gtk_widget_destroy(dialog);
1267
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001268 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001269 goto err;
1270
Jens Axboe0420ba62012-02-29 11:16:52 +01001271 filenames = fn_glist;
1272 while (filenames != NULL) {
Jens Axboe0420ba62012-02-29 11:16:52 +01001273 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1274 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1275 ui.nr_job_files++;
1276
Jens Axboea5276612012-03-04 15:15:08 +01001277 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
Jens Axboedf06f222012-03-02 13:32:04 +01001278 if (!ui.client) {
1279 GError *error;
1280
1281 error = g_error_new(g_quark_from_string("fio"), 1,
1282 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001283 report_error(error);
1284 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001285 }
Jens Axboedf06f222012-03-02 13:32:04 +01001286 ui.client->client_data = &ui;
Jens Axboe0420ba62012-02-29 11:16:52 +01001287
1288 g_free(filenames->data);
1289 filenames = g_slist_next(filenames);
1290 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001291 free(host);
1292err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001293 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001294}
1295
1296static void file_save(GtkWidget *w, gpointer data)
1297{
1298 GtkWidget *dialog;
1299
1300 dialog = gtk_file_chooser_dialog_new("Save File",
1301 GTK_WINDOW(ui.window),
1302 GTK_FILE_CHOOSER_ACTION_SAVE,
1303 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1304 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1305 NULL);
1306
1307 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1308 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1309
1310 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1311 char *filename;
1312
1313 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1314 // save_job_file(filename);
1315 g_free(filename);
1316 }
1317 gtk_widget_destroy(dialog);
1318}
1319
Jens Axboe46974a72012-03-02 19:34:13 +01001320static void preferences(GtkWidget *w, gpointer data)
1321{
1322 GtkWidget *dialog, *frame, *box, **buttons;
1323 int i;
1324
1325 dialog = gtk_dialog_new_with_buttons("Preferences",
1326 GTK_WINDOW(ui.window),
1327 GTK_DIALOG_DESTROY_WITH_PARENT,
1328 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1329 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1330 NULL);
1331
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001332 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01001333 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1334 box = gtk_hbox_new(FALSE, 6);
1335 gtk_container_add(GTK_CONTAINER(frame), box);
1336
1337 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1338
1339 for (i = 0; i < FD_DEBUG_MAX; i++) {
1340 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001341 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01001342 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1343 }
1344
1345 gtk_widget_show_all(dialog);
1346
1347 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1348 gtk_widget_destroy(dialog);
1349 return;
1350 }
1351
1352 for (i = 0; i < FD_DEBUG_MAX; i++) {
1353 int set;
1354
1355 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1356 if (set)
1357 fio_debug |= (1UL << i);
1358 }
1359
1360 gtk_widget_destroy(dialog);
1361}
1362
Jens Axboe0420ba62012-02-29 11:16:52 +01001363static void about_dialog(GtkWidget *w, gpointer data)
1364{
1365 gtk_show_about_dialog(NULL,
1366 "program-name", "gfio",
1367 "comments", "Gtk2 UI for fio",
1368 "license", "GPLv2",
1369 "version", fio_version_string,
1370 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1371 "logo-icon-name", "fio",
1372 /* Must be last: */
1373 NULL, NULL,
1374 NULL);
1375}
1376
1377static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001378 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1379 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1380 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1381 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1382 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1383 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1384 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001385};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001386static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001387
1388static const gchar *ui_string = " \
1389 <ui> \
1390 <menubar name=\"MainMenu\"> \
1391 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1392 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1393 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1394 <separator name=\"Separator\"/> \
Jens Axboe46974a72012-03-02 19:34:13 +01001395 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1396 <separator name=\"Separator2\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001397 <menuitem name=\"Quit\" action=\"Quit\" /> \
1398 </menu> \
1399 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1400 <menuitem name=\"About\" action=\"About\" /> \
1401 </menu> \
1402 </menubar> \
1403 </ui> \
1404";
1405
1406static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1407{
1408 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1409 GError *error = 0;
1410
1411 action_group = gtk_action_group_new("Menu");
1412 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1413
1414 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1415 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1416
1417 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1418 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1419}
1420
1421void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1422 GtkWidget *vbox, GtkUIManager *ui_manager)
1423{
1424 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1425}
1426
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001427static void init_ui(int *argc, char **argv[], struct gui *ui)
1428{
Jens Axboe0420ba62012-02-29 11:16:52 +01001429 GtkSettings *settings;
1430 GtkUIManager *uimanager;
Jens Axboe843ad232012-02-29 11:44:53 +01001431 GtkWidget *menu, *probe, *probe_frame, *probe_box;
Jens Axboe0420ba62012-02-29 11:16:52 +01001432
1433 memset(ui, 0, sizeof(*ui));
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001434
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001435 /* Magical g*thread incantation, you just need this thread stuff.
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001436 * Without it, the update that happens in gfio_update_thread_status
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001437 * doesn't really happen in a timely fashion, you need expose events
1438 */
Jens Axboeed727a42012-03-02 12:14:40 +01001439 if (!g_thread_supported())
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001440 g_thread_init(NULL);
1441 gdk_threads_init();
1442
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001443 gtk_init(argc, argv);
Jens Axboe0420ba62012-02-29 11:16:52 +01001444 settings = gtk_settings_get_default();
1445 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1446 g_type_init();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001447
1448 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1449 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1450 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1451
Jens Axboe0420ba62012-02-29 11:16:52 +01001452 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1453 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001454
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001455 ui->vbox = gtk_vbox_new(FALSE, 0);
1456 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001457
Jens Axboe0420ba62012-02-29 11:16:52 +01001458 uimanager = gtk_ui_manager_new();
1459 menu = get_menubar_menu(ui->window, uimanager);
1460 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1461
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001462 /*
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001463 * Set up alignments for widgets at the top of ui,
1464 * align top left, expand horizontally but not vertically
1465 */
1466 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001467 ui->topvbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001468 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001469 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001470
Jens Axboe3e47bd22012-02-29 13:45:02 +01001471 probe = gtk_frame_new("Job");
Jens Axboe843ad232012-02-29 11:44:53 +01001472 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1473 probe_frame = gtk_vbox_new(FALSE, 3);
1474 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1475
1476 probe_box = gtk_hbox_new(FALSE, 3);
1477 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001478 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1479 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1480 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1481 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1482
Jens Axboe3e47bd22012-02-29 13:45:02 +01001483 probe_box = gtk_hbox_new(FALSE, 3);
1484 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001485
Jens Axboeca850992012-03-05 20:04:43 +01001486 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1487 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1488 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1489 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1490 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1491 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001492
1493 probe_box = gtk_hbox_new(FALSE, 3);
1494 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01001495 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1496 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1497 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1498 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001499
1500 /*
1501 * Only add this if we have a commit rate
1502 */
1503#if 0
1504 probe_box = gtk_hbox_new(FALSE, 3);
1505 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1506
Jens Axboe3e47bd22012-02-29 13:45:02 +01001507 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1508 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1509
Jens Axboe3e47bd22012-02-29 13:45:02 +01001510 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1511 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001512#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001513
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001514 /*
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001515 * Add a text box for text op messages
1516 */
1517 ui->textview = gtk_text_view_new();
1518 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1519 gtk_text_buffer_set_text(ui->text, "", -1);
1520 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1521 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1522 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1523 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1524 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1525 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001526 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1527 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001528
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001529 /*
1530 * Set up alignments for widgets at the bottom of ui,
1531 * align bottom left, expand horizontally but not vertically
1532 */
1533 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1534 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1535 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001536 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1537 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001538
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001539 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001540
1541 /*
1542 * Set up thread status progress bar
1543 */
1544 ui->thread_status_pb = gtk_progress_bar_new();
1545 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001546 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001547 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1548
1549
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001550 gtk_widget_show_all(ui->window);
1551}
1552
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001553int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001554{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001555 if (initialize_fio(envp))
1556 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001557 if (fio_init_options())
1558 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001559
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001560 init_ui(&argc, &argv, &ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001561
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001562 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001563 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001564 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001565 return 0;
1566}