blob: c7160ada468d139ea110fed0dba786e41af6601a [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;
Jens Axboef9d40b42012-03-06 09:52:49 +010091 GtkWidget *results_notebook;
92 GtkWidget *results_window;
Stephen M. Cameron736f2df2012-02-24 08:17:32 +010093 GtkTextBuffer *text;
Jens Axboe843ad232012-02-29 11:44:53 +010094 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +010095 struct eta_widget eta;
Jens Axboe3ec62ec2012-03-01 12:01:29 +010096 int connected;
Stephen M. Cameron25927252012-02-24 08:17:31 +010097 pthread_t t;
Jens Axboe0420ba62012-02-29 11:16:52 +010098
Jens Axboe3ec62ec2012-03-01 12:01:29 +010099 struct fio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100100 int nr_job_files;
101 char **job_files;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100102} ui;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100103
Jens Axboe8663ea62012-03-02 14:04:30 +0100104static void clear_ui_info(struct gui *ui)
105{
106 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
107 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
108 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
109 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
Jens Axboeca850992012-03-05 20:04:43 +0100110 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
111 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
112 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
113 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
114 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
115 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
116 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
117 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
118 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
119 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100120}
121
Jens Axboe3650a3c2012-03-05 14:09:03 +0100122static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
123{
124 GtkWidget *entry, *frame;
125
126 frame = gtk_frame_new(label);
127 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100128 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100129 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
130 gtk_container_add(GTK_CONTAINER(frame), entry);
131
132 return entry;
133}
134
135static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
136{
137 GtkWidget *label_widget;
138 GtkWidget *frame;
139
140 frame = gtk_frame_new(label);
141 label_widget = gtk_label_new(NULL);
142 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
143 gtk_container_add(GTK_CONTAINER(frame), label_widget);
144
145 return label_widget;
146}
147
148static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
149{
150 GtkWidget *button, *box;
151
152 box = gtk_hbox_new(FALSE, 3);
153 gtk_container_add(GTK_CONTAINER(hbox), box);
154
155 button = gtk_spin_button_new_with_range(min, max, 1.0);
156 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
157
158 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
159 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
160
161 return button;
162}
163
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100164static void gfio_set_connected(struct gui *ui, int connected)
165{
166 if (connected) {
167 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
168 ui->connected = 1;
169 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
170 } else {
171 ui->connected = 0;
172 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
173 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
174 }
175}
176
Jens Axboe3650a3c2012-03-05 14:09:03 +0100177static void label_set_int_value(GtkWidget *entry, unsigned int val)
178{
179 char tmp[80];
180
181 sprintf(tmp, "%u", val);
182 gtk_label_set_text(GTK_LABEL(entry), tmp);
183}
184
185static void entry_set_int_value(GtkWidget *entry, unsigned int val)
186{
187 char tmp[80];
188
189 sprintf(tmp, "%u", val);
190 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
191}
192
Jens Axboea2697902012-03-05 16:43:49 +0100193#define ALIGN_LEFT 1
194#define ALIGN_RIGHT 2
195#define INVISIBLE 4
196#define UNSORTABLE 8
197
198GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
199{
200 GtkCellRenderer *renderer;
201 GtkTreeViewColumn *col;
202 double xalign = 0.0; /* left as default */
203 PangoAlignment align;
204 gboolean visible;
205
206 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
207 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
208 PANGO_ALIGN_CENTER;
209 visible = !(flags & INVISIBLE);
210
211 renderer = gtk_cell_renderer_text_new();
212 col = gtk_tree_view_column_new();
213
214 gtk_tree_view_column_set_title(col, title);
215 if (!(flags & UNSORTABLE))
216 gtk_tree_view_column_set_sort_column_id(col, index);
217 gtk_tree_view_column_set_resizable(col, TRUE);
218 gtk_tree_view_column_pack_start(col, renderer, TRUE);
219 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
220 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
221 switch (align) {
222 case PANGO_ALIGN_LEFT:
223 xalign = 0.0;
224 break;
225 case PANGO_ALIGN_CENTER:
226 xalign = 0.5;
227 break;
228 case PANGO_ALIGN_RIGHT:
229 xalign = 1.0;
230 break;
231 }
232 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
233 gtk_tree_view_column_set_visible(col, visible);
234 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
235 return col;
236}
237
238static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
239 fio_fp64_t *plist,
240 unsigned int len,
241 const char *base,
242 unsigned int scale)
243{
244 GType types[FIO_IO_U_LIST_MAX_LEN];
245 GtkWidget *tree_view;
246 GtkTreeSelection *selection;
247 GtkListStore *model;
248 GtkTreeIter iter;
249 int i;
250
251 for (i = 0; i < len; i++)
252 types[i] = G_TYPE_INT;
253
254 model = gtk_list_store_newv(len, types);
255
256 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
257 gtk_widget_set_can_focus(tree_view, FALSE);
258
259 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
260 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
261
262 for (i = 0; i < len; i++) {
263 char fbuf[8];
264
265 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
266 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
267 }
268
269 gtk_list_store_append(model, &iter);
270
271 for (i = 0; i < len; i++)
272 gtk_list_store_set(model, &iter, i, ovals[i], -1);
273
274 return tree_view;
275}
276
277static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
278 int ddir)
279{
280 unsigned int *io_u_plat = ts->io_u_plat[ddir];
281 unsigned long nr = ts->clat_stat[ddir].samples;
282 fio_fp64_t *plist = ts->percentile_list;
283 unsigned int *ovals, len, minv, maxv, scale_down;
284 const char *base;
285 GtkWidget *tree_view, *frame, *hbox;
286 char tmp[64];
287
288 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
289 if (!len)
290 goto out;
291
292 /*
293 * We default to usecs, but if the value range is such that we
294 * should scale down to msecs, do that.
295 */
296 if (minv > 2000 && maxv > 99999) {
297 scale_down = 1;
298 base = "msec";
299 } else {
300 scale_down = 0;
301 base = "usec";
302 }
303
304 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
305
306 sprintf(tmp, "Completion percentiles (%s)", base);
307 frame = gtk_frame_new(tmp);
308 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
309
310 hbox = gtk_hbox_new(FALSE, 3);
311 gtk_container_add(GTK_CONTAINER(frame), hbox);
312
313 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
314out:
315 if (ovals)
316 free(ovals);
317}
318
Jens Axboe3650a3c2012-03-05 14:09:03 +0100319static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
320 unsigned long max, double mean, double dev)
321{
322 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100323 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100324 char *minp, *maxp;
325 char tmp[64];
326
327 if (!usec_to_msec(&min, &max, &mean, &dev))
328 base = "(msec)";
329
330 minp = num2str(min, 6, 1, 0);
331 maxp = num2str(max, 6, 1, 0);
332
Jens Axboe3650a3c2012-03-05 14:09:03 +0100333 sprintf(tmp, "%s %s", name, base);
334 frame = gtk_frame_new(tmp);
335 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
336
Jens Axboe3650a3c2012-03-05 14:09:03 +0100337 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100338 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100339
340 label = new_info_label_in_frame(hbox, "Minimum");
341 gtk_label_set_text(GTK_LABEL(label), minp);
342 label = new_info_label_in_frame(hbox, "Maximum");
343 gtk_label_set_text(GTK_LABEL(label), maxp);
344 label = new_info_label_in_frame(hbox, "Average");
345 sprintf(tmp, "%5.02f", mean);
346 gtk_label_set_text(GTK_LABEL(label), tmp);
347 label = new_info_label_in_frame(hbox, "Standard deviation");
348 sprintf(tmp, "%5.02f", dev);
349 gtk_label_set_text(GTK_LABEL(label), tmp);
350
351 free(minp);
352 free(maxp);
353
354}
355
Jens Axboeca850992012-03-05 20:04:43 +0100356#define GFIO_CLAT 1
357#define GFIO_SLAT 2
358#define GFIO_LAT 4
359
Jens Axboe3650a3c2012-03-05 14:09:03 +0100360static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
361 struct thread_stat *ts, int ddir)
362{
363 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100364 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100365 unsigned long min, max, runt;
366 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100367 unsigned int flags = 0;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100368 double mean, dev;
369 char *io_p, *bw_p, *iops_p;
370 int i2p;
371
372 if (!ts->runtime[ddir])
373 return;
374
375 i2p = is_power_of_2(rs->kb_base);
376 runt = ts->runtime[ddir];
377
378 bw = (1000 * ts->io_bytes[ddir]) / runt;
379 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
380 bw_p = num2str(bw, 6, 1, i2p);
381
382 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
383 iops_p = num2str(iops, 6, 1, 0);
384
385 box = gtk_hbox_new(FALSE, 3);
386 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
387
388 frame = gtk_frame_new(ddir_label[ddir]);
389 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
390
Jens Axboe0b761302012-03-05 20:44:11 +0100391 main_vbox = gtk_vbox_new(FALSE, 3);
392 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100393
394 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100395 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100396
397 label = new_info_label_in_frame(box, "IO");
398 gtk_label_set_text(GTK_LABEL(label), io_p);
399 label = new_info_label_in_frame(box, "Bandwidth");
400 gtk_label_set_text(GTK_LABEL(label), bw_p);
401 label = new_info_label_in_frame(box, "IOPS");
402 gtk_label_set_text(GTK_LABEL(label), iops_p);
403 label = new_info_label_in_frame(box, "Runtime (msec)");
404 label_set_int_value(label, ts->runtime[ddir]);
405
Jens Axboeca850992012-03-05 20:04:43 +0100406 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
407 double p_of_agg = 100.0;
408 const char *bw_str = "KB";
409 char tmp[32];
410
411 if (rs->agg[ddir]) {
412 p_of_agg = mean * 100 / (double) rs->agg[ddir];
413 if (p_of_agg > 100.0)
414 p_of_agg = 100.0;
415 }
416
417 if (mean > 999999.9) {
418 min /= 1000.0;
419 max /= 1000.0;
420 mean /= 1000.0;
421 dev /= 1000.0;
422 bw_str = "MB";
423 }
424
Jens Axboe0b761302012-03-05 20:44:11 +0100425 sprintf(tmp, "Bandwidth (%s)", bw_str);
426 frame = gtk_frame_new(tmp);
427 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100428
Jens Axboe0b761302012-03-05 20:44:11 +0100429 box = gtk_hbox_new(FALSE, 3);
430 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100431
Jens Axboe0b761302012-03-05 20:44:11 +0100432 label = new_info_label_in_frame(box, "Minimum");
Jens Axboeca850992012-03-05 20:04:43 +0100433 label_set_int_value(label, min);
Jens Axboe0b761302012-03-05 20:44:11 +0100434 label = new_info_label_in_frame(box, "Maximum");
Jens Axboeca850992012-03-05 20:04:43 +0100435 label_set_int_value(label, max);
Jens Axboe0b761302012-03-05 20:44:11 +0100436 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100437 sprintf(tmp, "%3.2f%%", p_of_agg);
438 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100439 label = new_info_label_in_frame(box, "Average");
Jens Axboeca850992012-03-05 20:04:43 +0100440 sprintf(tmp, "%5.02f", mean);
441 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100442 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboeca850992012-03-05 20:04:43 +0100443 sprintf(tmp, "%5.02f", dev);
444 gtk_label_set_text(GTK_LABEL(label), tmp);
445 }
446
Jens Axboe2b089892012-03-06 08:09:17 +0100447 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
448 flags |= GFIO_SLAT;
449 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
450 flags |= GFIO_CLAT;
451 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
452 flags |= GFIO_LAT;
453
454 if (flags) {
455 frame = gtk_frame_new("Latency");
456 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
457
458 vbox = gtk_vbox_new(FALSE, 3);
459 gtk_container_add(GTK_CONTAINER(frame), vbox);
460
461 if (flags & GFIO_SLAT)
462 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
463 if (flags & GFIO_CLAT)
464 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
465 if (flags & GFIO_LAT)
466 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
467 }
468
469 if (ts->clat_percentiles)
470 gfio_show_clat_percentiles(main_vbox, ts, ddir);
471
472
Jens Axboe3650a3c2012-03-05 14:09:03 +0100473 free(io_p);
474 free(bw_p);
475 free(iops_p);
476}
477
Jens Axboee5bd1342012-03-05 21:38:12 +0100478static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
479 const char **labels)
480{
481 GtkWidget *tree_view;
482 GtkTreeSelection *selection;
483 GtkListStore *model;
484 GtkTreeIter iter;
485 GType *types;
486 int i, skipped;
487
488 /*
489 * Check if all are empty, in which case don't bother
490 */
491 for (i = 0, skipped = 0; i < num; i++)
492 if (lat[i] <= 0.0)
493 skipped++;
494
495 if (skipped == num)
496 return NULL;
497
498 types = malloc(num * sizeof(GType));
499
500 for (i = 0; i < num; i++)
501 types[i] = G_TYPE_STRING;
502
503 model = gtk_list_store_newv(num, types);
504 free(types);
505 types = NULL;
506
507 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
508 gtk_widget_set_can_focus(tree_view, FALSE);
509
510 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
511 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
512
513 for (i = 0; i < num; i++)
514 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
515
516 gtk_list_store_append(model, &iter);
517
518 for (i = 0; i < num; i++) {
519 char fbuf[32];
520
521 if (lat[i] <= 0.0)
522 sprintf(fbuf, "0.00");
523 else
524 sprintf(fbuf, "%3.2f%%", lat[i]);
525
526 gtk_list_store_set(model, &iter, i, fbuf, -1);
527 }
528
529 return tree_view;
530}
531
532static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
533{
534 GtkWidget *box, *frame, *tree_view;
535 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
536 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
537 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
538 "250", "500", "750", "1000", };
539 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
540 "250", "500", "750", "1000", "2000",
541 ">= 2000", };
542
543 stat_calc_lat_u(ts, io_u_lat_u);
544 stat_calc_lat_m(ts, io_u_lat_m);
545
546 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
547 if (tree_view) {
548 frame = gtk_frame_new("Latency buckets (usec)");
549 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
550
551 box = gtk_hbox_new(FALSE, 3);
552 gtk_container_add(GTK_CONTAINER(frame), box);
553 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
554 }
555
556 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
557 if (tree_view) {
558 frame = gtk_frame_new("Latency buckets (msec)");
559 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
560
561 box = gtk_hbox_new(FALSE, 3);
562 gtk_container_add(GTK_CONTAINER(frame), box);
563 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
564 }
565}
566
Jens Axboe2e331012012-03-05 22:07:54 +0100567static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
568{
569 GtkWidget *box, *frame, *entry;
570 double usr_cpu, sys_cpu;
571 unsigned long runtime;
572 char tmp[32];
573
574 runtime = ts->total_run_time;
575 if (runtime) {
576 double runt = (double) runtime;
577
578 usr_cpu = (double) ts->usr_time * 100 / runt;
579 sys_cpu = (double) ts->sys_time * 100 / runt;
580 } else {
581 usr_cpu = 0;
582 sys_cpu = 0;
583 }
584
585 frame = gtk_frame_new("OS resources");
586 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587
588 box = gtk_hbox_new(FALSE, 3);
589 gtk_container_add(GTK_CONTAINER(frame), box);
590
591 entry = new_info_entry_in_frame(box, "User CPU");
592 sprintf(tmp, "%3.2f%%", usr_cpu);
593 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
594 entry = new_info_entry_in_frame(box, "System CPU");
595 sprintf(tmp, "%3.2f%%", sys_cpu);
596 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
597 entry = new_info_entry_in_frame(box, "Context switches");
598 entry_set_int_value(entry, ts->ctx);
599 entry = new_info_entry_in_frame(box, "Major faults");
600 entry_set_int_value(entry, ts->majf);
601 entry = new_info_entry_in_frame(box, "Minor faults");
602 entry_set_int_value(entry, ts->minf);
603}
Jens Axboe19998db2012-03-06 09:17:59 +0100604static void gfio_add_sc_depths_tree(GtkListStore *model,
605 struct thread_stat *ts, unsigned int len,
606 int submit)
607{
608 double io_u_dist[FIO_IO_U_MAP_NR];
609 GtkTreeIter iter;
610 /* Bits 0, and 3-8 */
611 const int add_mask = 0x1f9;
612 int i, j;
613
614 if (submit)
615 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
616 else
617 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
618
619 gtk_list_store_append(model, &iter);
620
621 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
622
623 for (i = 1, j = 0; i < len; i++) {
624 char fbuf[32];
625
626 if (!(add_mask & (1UL << (i - 1))))
627 sprintf(fbuf, "0.0%%");
628 else {
629 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
630 j++;
631 }
632
633 gtk_list_store_set(model, &iter, i, fbuf, -1);
634 }
635
636}
637
638static void gfio_add_total_depths_tree(GtkListStore *model,
639 struct thread_stat *ts, unsigned int len)
640{
641 double io_u_dist[FIO_IO_U_MAP_NR];
642 GtkTreeIter iter;
643 /* Bits 1-6, and 8 */
644 const int add_mask = 0x17e;
645 int i, j;
646
647 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
648
649 gtk_list_store_append(model, &iter);
650
651 gtk_list_store_set(model, &iter, 0, "Total", -1);
652
653 for (i = 1, j = 0; i < len; i++) {
654 char fbuf[32];
655
656 if (!(add_mask & (1UL << (i - 1))))
657 sprintf(fbuf, "0.0%%");
658 else {
659 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
660 j++;
661 }
662
663 gtk_list_store_set(model, &iter, i, fbuf, -1);
664 }
665
666}
Jens Axboe2e331012012-03-05 22:07:54 +0100667
668static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
669{
Jens Axboe2e331012012-03-05 22:07:54 +0100670 GtkWidget *frame, *box, *tree_view;
671 GtkTreeSelection *selection;
672 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100673 GType types[FIO_IO_U_MAP_NR + 1];
674 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100675#define NR_LABELS 10
676 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100677
678 frame = gtk_frame_new("IO depths");
679 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
680
681 box = gtk_hbox_new(FALSE, 3);
682 gtk_container_add(GTK_CONTAINER(frame), box);
683
Jens Axboe19998db2012-03-06 09:17:59 +0100684 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100685 types[i] = G_TYPE_STRING;
686
Jens Axboe19998db2012-03-06 09:17:59 +0100687 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100688
689 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
690 gtk_widget_set_can_focus(tree_view, FALSE);
691
692 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
693 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
694
Jens Axboe19998db2012-03-06 09:17:59 +0100695 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100696 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
697
Jens Axboe19998db2012-03-06 09:17:59 +0100698 gfio_add_total_depths_tree(model, ts, NR_LABELS);
699 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
700 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100701
702 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
703}
704
Jens Axboef9d40b42012-03-06 09:52:49 +0100705static gboolean results_window_delete(GtkWidget *w, gpointer data)
706{
707 struct gui *ui = (struct gui *) data;
708
709 gtk_widget_destroy(w);
710 ui->results_window = NULL;
711 ui->results_notebook = NULL;
712 return TRUE;
713}
714
715static GtkWidget *get_results_window(struct gui *ui)
716{
717 GtkWidget *win, *notebook;
718
719 if (ui->results_window)
720 return ui->results_notebook;
721
722 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
723 gtk_window_set_title(GTK_WINDOW(win), "Results");
724 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
725 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
726
727 notebook = gtk_notebook_new();
728 gtk_container_add(GTK_CONTAINER(win), notebook);
729
730 ui->results_window = win;
731 ui->results_notebook = notebook;
732 return ui->results_notebook;
733}
734
Jens Axboe3650a3c2012-03-05 14:09:03 +0100735static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
736 struct group_run_stats *rs)
737{
Jens Axboef9d40b42012-03-06 09:52:49 +0100738 GtkWidget *res_win, *box, *vbox, *entry;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100739 struct gui *ui = client->client_data;
740
741 gdk_threads_enter();
742
Jens Axboef9d40b42012-03-06 09:52:49 +0100743 res_win = get_results_window(ui);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100744
745 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100746
747 box = gtk_hbox_new(TRUE, 3);
748 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
749
Jens Axboef9d40b42012-03-06 09:52:49 +0100750 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
751
Jens Axboe3650a3c2012-03-05 14:09:03 +0100752 entry = new_info_entry_in_frame(box, "Name");
753 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
754 if (strlen(ts->description)) {
755 entry = new_info_entry_in_frame(box, "Description");
756 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
757 }
758 entry = new_info_entry_in_frame(box, "Group ID");
759 entry_set_int_value(entry, ts->groupid);
760 entry = new_info_entry_in_frame(box, "Jobs");
761 entry_set_int_value(entry, ts->members);
762 entry = new_info_entry_in_frame(box, "Error");
763 entry_set_int_value(entry, ts->error);
764 entry = new_info_entry_in_frame(box, "PID");
765 entry_set_int_value(entry, ts->pid);
766
767 if (ts->io_bytes[DDIR_READ])
768 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
769 if (ts->io_bytes[DDIR_WRITE])
770 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
771
Jens Axboee5bd1342012-03-05 21:38:12 +0100772 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100773 gfio_show_cpu_usage(vbox, ts);
774 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100775
Jens Axboef9d40b42012-03-06 09:52:49 +0100776 gtk_widget_show_all(ui->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100777 gdk_threads_leave();
778}
779
Jens Axboe084d1c62012-03-03 20:28:07 +0100780static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100781{
Jens Axboe807f9972012-03-02 10:25:24 +0100782#if 0
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100783 GtkTextBuffer *buffer;
784 GtkTextIter end;
785
786 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
787 gdk_threads_enter();
788 gtk_text_buffer_get_end_iter(buffer, &end);
789 gtk_text_buffer_insert(buffer, &end, buf, -1);
790 gdk_threads_leave();
791 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
792 &end, 0.0, FALSE, 0.0,0.0);
Jens Axboe807f9972012-03-02 10:25:24 +0100793#else
Jens Axboe084d1c62012-03-03 20:28:07 +0100794 fio_client_ops.text_op(client, cmd);
Jens Axboe807f9972012-03-02 10:25:24 +0100795#endif
Stephen M. Camerona1820202012-02-24 08:17:31 +0100796}
797
798static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
799{
Jens Axboe0050e5f2012-03-06 09:23:27 +0100800 gdk_threads_enter();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100801 printf("gfio_disk_util_op called\n");
802 fio_client_ops.disk_util(client, cmd);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100803 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100804}
805
Jens Axboe3650a3c2012-03-05 14:09:03 +0100806extern int sum_stat_clients;
807extern struct thread_stat client_ts;
808extern struct group_run_stats client_gs;
809
810static int sum_stat_nr;
811
Jens Axboe89e5fad2012-03-05 09:21:12 +0100812static void gfio_thread_status_op(struct fio_client *client,
813 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100814{
Jens Axboe3650a3c2012-03-05 14:09:03 +0100815 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
816
817 gfio_display_ts(client, &p->ts, &p->rs);
818
819 if (sum_stat_clients == 1)
820 return;
821
822 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
823 sum_group_stats(&client_gs, &p->rs);
824
825 client_ts.members++;
826 client_ts.groupid = p->ts.groupid;
827
828 if (++sum_stat_nr == sum_stat_clients) {
829 strcpy(client_ts.name, "All clients");
830 gfio_display_ts(client, &client_ts, &client_gs);
831 }
Stephen M. Camerona1820202012-02-24 08:17:31 +0100832}
833
Jens Axboe89e5fad2012-03-05 09:21:12 +0100834static void gfio_group_stats_op(struct fio_client *client,
835 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100836{
Jens Axboe0050e5f2012-03-06 09:23:27 +0100837 gdk_threads_enter();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100838 printf("gfio_group_stats_op called\n");
Jens Axboe89e5fad2012-03-05 09:21:12 +0100839 fio_client_ops.group_stats(client, cmd);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100840 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100841}
842
Jens Axboe3e47bd22012-02-29 13:45:02 +0100843static void gfio_update_eta(struct jobs_eta *je)
844{
845 static int eta_good;
846 char eta_str[128];
847 char output[256];
848 char tmp[32];
849 double perc = 0.0;
850 int i2p = 0;
851
Jens Axboe0050e5f2012-03-06 09:23:27 +0100852 gdk_threads_enter();
853
Jens Axboe3e47bd22012-02-29 13:45:02 +0100854 eta_str[0] = '\0';
855 output[0] = '\0';
856
857 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
858 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
859 eta_to_str(eta_str, je->eta_sec);
860 }
861
862 sprintf(tmp, "%u", je->nr_running);
Jens Axboeca850992012-03-05 20:04:43 +0100863 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100864 sprintf(tmp, "%u", je->files_open);
Jens Axboeca850992012-03-05 20:04:43 +0100865 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100866
867#if 0
868 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
869 if (je->m_rate || je->t_rate) {
870 char *tr, *mr;
871
872 mr = num2str(je->m_rate, 4, 0, i2p);
873 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboeca850992012-03-05 20:04:43 +0100874 gtk_entry_set_text(GTK_ENTRY(ui.eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100875 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
876 free(tr);
877 free(mr);
878 } else if (je->m_iops || je->t_iops)
879 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +0100880
Jens Axboeca850992012-03-05 20:04:43 +0100881 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
882 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
883 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
884 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +0100885#endif
886
887 if (je->eta_sec != INT_MAX && je->nr_running) {
888 char *iops_str[2];
889 char *rate_str[2];
890
891 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
892 strcpy(output, "-.-% done");
893 else {
894 eta_good = 1;
895 perc *= 100.0;
896 sprintf(output, "%3.1f%% done", perc);
897 }
898
899 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
900 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
901
902 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
903 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
904
Jens Axboeca850992012-03-05 20:04:43 +0100905 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
906 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
907 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
908 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100909
910 free(rate_str[0]);
911 free(rate_str[1]);
912 free(iops_str[0]);
913 free(iops_str[1]);
914 }
915
916 if (eta_str[0]) {
917 char *dst = output + strlen(output);
918
919 sprintf(dst, " - %s", eta_str);
920 }
921
922 gfio_update_thread_status(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100923 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +0100924}
925
Stephen M. Camerona1820202012-02-24 08:17:31 +0100926static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
927{
Jens Axboe843ad232012-02-29 11:44:53 +0100928 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
929 const char *os, *arch;
930 char buf[64];
931
932 os = fio_get_os_string(probe->os);
933 if (!os)
934 os = "unknown";
935
936 arch = fio_get_arch_string(probe->arch);
937 if (!arch)
938 os = "unknown";
939
940 if (!client->name)
941 client->name = strdup((char *) probe->hostname);
942
Jens Axboe0050e5f2012-03-06 09:23:27 +0100943 gdk_threads_enter();
944
Jens Axboe843ad232012-02-29 11:44:53 +0100945 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
946 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
947 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
948 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
949 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100950
951 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100952}
953
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100954static void gfio_update_thread_status(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100955{
956 static char message[100];
957 const char *m = message;
958
959 strncpy(message, status_message, sizeof(message) - 1);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100960 gtk_progress_bar_set_text(
961 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
962 gtk_progress_bar_set_fraction(
963 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100964 gtk_widget_queue_draw(ui.window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100965}
966
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100967static void gfio_quit_op(struct fio_client *client)
968{
969 struct gui *ui = client->client_data;
970
Jens Axboe0050e5f2012-03-06 09:23:27 +0100971 gdk_threads_enter();
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100972 gfio_set_connected(ui, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +0100973 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100974}
975
Jens Axboe807f9972012-03-02 10:25:24 +0100976static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
977{
978 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
979 struct gui *ui = client->client_data;
980 char tmp[8];
981 int i;
982
983 p->iodepth = le32_to_cpu(p->iodepth);
984 p->rw = le32_to_cpu(p->rw);
985
986 for (i = 0; i < 2; i++) {
987 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
988 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
989 }
990
991 p->numjobs = le32_to_cpu(p->numjobs);
992 p->group_reporting = le32_to_cpu(p->group_reporting);
993
Jens Axboe0050e5f2012-03-06 09:23:27 +0100994 gdk_threads_enter();
995
Jens Axboeca850992012-03-05 20:04:43 +0100996 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
997 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
998 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +0100999
1000 sprintf(tmp, "%u", p->iodepth);
Jens Axboeca850992012-03-05 20:04:43 +01001001 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001002
1003 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001004}
1005
Jens Axboeed727a42012-03-02 12:14:40 +01001006static void gfio_client_timed_out(struct fio_client *client)
1007{
1008 struct gui *ui = client->client_data;
1009 GtkWidget *dialog, *label, *content;
1010 char buf[256];
1011
1012 gdk_threads_enter();
1013
1014 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +01001015 clear_ui_info(ui);
Jens Axboeed727a42012-03-02 12:14:40 +01001016
1017 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1018
1019 dialog = gtk_dialog_new_with_buttons("Timed out!",
1020 GTK_WINDOW(ui->window),
1021 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1022 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1023
1024 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1025 label = gtk_label_new((const gchar *) buf);
1026 gtk_container_add(GTK_CONTAINER(content), label);
1027 gtk_widget_show_all(dialog);
1028 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1029
1030 gtk_dialog_run(GTK_DIALOG(dialog));
1031 gtk_widget_destroy(dialog);
1032
1033 gdk_threads_leave();
1034}
1035
Stephen M. Camerona1820202012-02-24 08:17:31 +01001036struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001037 .text_op = gfio_text_op,
1038 .disk_util = gfio_disk_util_op,
1039 .thread_status = gfio_thread_status_op,
1040 .group_stats = gfio_group_stats_op,
Jens Axboea5276612012-03-04 15:15:08 +01001041 .eta = gfio_update_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001042 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001043 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001044 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001045 .timed_out = gfio_client_timed_out,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001046 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001047};
1048
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001049static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1050 __attribute__((unused)) gpointer data)
1051{
1052 gtk_main_quit();
1053}
1054
Stephen M. Cameron25927252012-02-24 08:17:31 +01001055static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001056{
Stephen M. Cameron25927252012-02-24 08:17:31 +01001057 fio_handle_clients(&gfio_client_ops);
Stephen M. Cameron25927252012-02-24 08:17:31 +01001058 return NULL;
1059}
1060
Jens Axboe0420ba62012-02-29 11:16:52 +01001061static int send_job_files(struct gui *ui)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001062{
Jens Axboe441013b2012-03-01 08:01:52 +01001063 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001064
Jens Axboe0420ba62012-02-29 11:16:52 +01001065 for (i = 0; i < ui->nr_job_files; i++) {
1066 ret = fio_clients_send_ini(ui->job_files[i]);
Jens Axboe441013b2012-03-01 08:01:52 +01001067 if (ret)
1068 break;
1069
Jens Axboe0420ba62012-02-29 11:16:52 +01001070 free(ui->job_files[i]);
1071 ui->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001072 }
1073 while (i < ui->nr_job_files) {
1074 free(ui->job_files[i]);
1075 ui->job_files[i] = NULL;
1076 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001077 }
1078
Jens Axboe441013b2012-03-01 08:01:52 +01001079 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001080}
1081
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001082static void start_job_thread(struct gui *ui)
Stephen M. Cameron25927252012-02-24 08:17:31 +01001083{
Jens Axboe0420ba62012-02-29 11:16:52 +01001084 if (send_job_files(ui)) {
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001085 printf("Yeah, I didn't really like those options too much.\n");
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001086 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1087 return;
1088 }
Stephen M. Cameron25927252012-02-24 08:17:31 +01001089}
1090
1091static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1092 gpointer data)
1093{
1094 struct gui *ui = data;
1095
Stephen M. Cameron25927252012-02-24 08:17:31 +01001096 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001097 start_job_thread(ui);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001098}
1099
Jens Axboedf06f222012-03-02 13:32:04 +01001100static void file_open(GtkWidget *w, gpointer data);
1101
1102static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001103{
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001104 struct gui *ui = data;
1105
1106 if (!ui->connected) {
Jens Axboedf06f222012-03-02 13:32:04 +01001107 if (!ui->nr_job_files)
1108 file_open(widget, data);
Jens Axboe8663ea62012-03-02 14:04:30 +01001109 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001110 fio_clients_connect();
1111 pthread_create(&ui->t, NULL, job_thread, NULL);
1112 gfio_set_connected(ui, 1);
Jens Axboedf06f222012-03-02 13:32:04 +01001113 } else {
1114 fio_clients_terminate();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001115 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +01001116 clear_ui_info(ui);
Jens Axboedf06f222012-03-02 13:32:04 +01001117 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001118}
1119
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001120static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1121 struct button_spec *buttonspec)
1122{
1123 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1124 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001125 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001126 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001127 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001128}
1129
1130static void add_buttons(struct gui *ui,
1131 struct button_spec *buttonlist,
1132 int nbuttons)
1133{
1134 int i;
1135
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001136 for (i = 0; i < nbuttons; i++)
1137 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1138}
1139
Jens Axboe0420ba62012-02-29 11:16:52 +01001140static void on_info_bar_response(GtkWidget *widget, gint response,
1141 gpointer data)
1142{
1143 if (response == GTK_RESPONSE_OK) {
1144 gtk_widget_destroy(widget);
1145 ui.error_info_bar = NULL;
1146 }
1147}
1148
Jens Axboedf06f222012-03-02 13:32:04 +01001149void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001150{
1151 if (ui.error_info_bar == NULL) {
1152 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1153 GTK_RESPONSE_OK,
1154 NULL);
1155 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1156 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1157 GTK_MESSAGE_ERROR);
1158
1159 ui.error_label = gtk_label_new(error->message);
1160 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1161 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1162
1163 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1164 gtk_widget_show_all(ui.vbox);
1165 } else {
1166 char buffer[256];
1167 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1168 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1169 }
1170}
1171
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001172static int get_connection_details(char **host, int *port, int *type,
1173 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001174{
1175 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001176 GtkWidget *button;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001177 char *typeentry;
1178
1179 dialog = gtk_dialog_new_with_buttons("Connection details",
1180 GTK_WINDOW(ui.window),
1181 GTK_DIALOG_DESTROY_WITH_PARENT,
1182 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1183 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1184
1185 frame = gtk_frame_new("Hostname / socket name");
1186 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1187 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1188
1189 box = gtk_vbox_new(FALSE, 6);
1190 gtk_container_add(GTK_CONTAINER(frame), box);
1191
1192 hbox = gtk_hbox_new(TRUE, 10);
1193 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1194 hentry = gtk_entry_new();
1195 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1196 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1197
1198 frame = gtk_frame_new("Port");
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 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1206
1207 frame = gtk_frame_new("Type");
1208 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1209 box = gtk_vbox_new(FALSE, 10);
1210 gtk_container_add(GTK_CONTAINER(frame), box);
1211
1212 hbox = gtk_hbox_new(TRUE, 4);
1213 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1214
1215 combo = gtk_combo_box_text_new();
1216 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1217 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1218 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1219 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1220
1221 gtk_container_add(GTK_CONTAINER(hbox), combo);
1222
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001223 frame = gtk_frame_new("Options");
1224 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1225 box = gtk_vbox_new(FALSE, 10);
1226 gtk_container_add(GTK_CONTAINER(frame), box);
1227
1228 hbox = gtk_hbox_new(TRUE, 4);
1229 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1230
1231 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1233 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.");
1234 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1235
Jens Axboea7a42ce2012-03-02 13:12:04 +01001236 gtk_widget_show_all(dialog);
1237
1238 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1239 gtk_widget_destroy(dialog);
1240 return 1;
1241 }
1242
1243 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1244 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1245
1246 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1247 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1248 *type = Fio_client_ipv4;
1249 else if (!strncmp(typeentry, "IPv6", 4))
1250 *type = Fio_client_ipv6;
1251 else
1252 *type = Fio_client_socket;
1253 g_free(typeentry);
1254
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001255 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1256
Jens Axboea7a42ce2012-03-02 13:12:04 +01001257 gtk_widget_destroy(dialog);
1258 return 0;
1259}
1260
Jens Axboe0420ba62012-02-29 11:16:52 +01001261static void file_open(GtkWidget *w, gpointer data)
1262{
1263 GtkWidget *dialog;
1264 GSList *filenames, *fn_glist;
1265 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001266 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001267 int port, type, server_start;
Jens Axboe0420ba62012-02-29 11:16:52 +01001268
1269 dialog = gtk_file_chooser_dialog_new("Open File",
1270 GTK_WINDOW(ui.window),
1271 GTK_FILE_CHOOSER_ACTION_OPEN,
1272 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1273 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1274 NULL);
1275 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1276
1277 filter = gtk_file_filter_new();
1278 gtk_file_filter_add_pattern(filter, "*.fio");
1279 gtk_file_filter_add_pattern(filter, "*.job");
1280 gtk_file_filter_add_mime_type(filter, "text/fio");
1281 gtk_file_filter_set_name(filter, "Fio job file");
1282 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1283
1284 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1285 gtk_widget_destroy(dialog);
1286 return;
1287 }
1288
1289 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001290
1291 gtk_widget_destroy(dialog);
1292
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001293 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001294 goto err;
1295
Jens Axboe0420ba62012-02-29 11:16:52 +01001296 filenames = fn_glist;
1297 while (filenames != NULL) {
Jens Axboe0420ba62012-02-29 11:16:52 +01001298 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1299 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1300 ui.nr_job_files++;
1301
Jens Axboea5276612012-03-04 15:15:08 +01001302 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
Jens Axboedf06f222012-03-02 13:32:04 +01001303 if (!ui.client) {
1304 GError *error;
1305
1306 error = g_error_new(g_quark_from_string("fio"), 1,
1307 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001308 report_error(error);
1309 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001310 }
Jens Axboedf06f222012-03-02 13:32:04 +01001311 ui.client->client_data = &ui;
Jens Axboe0420ba62012-02-29 11:16:52 +01001312
1313 g_free(filenames->data);
1314 filenames = g_slist_next(filenames);
1315 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001316 free(host);
1317err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001318 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001319}
1320
1321static void file_save(GtkWidget *w, gpointer data)
1322{
1323 GtkWidget *dialog;
1324
1325 dialog = gtk_file_chooser_dialog_new("Save File",
1326 GTK_WINDOW(ui.window),
1327 GTK_FILE_CHOOSER_ACTION_SAVE,
1328 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1329 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1330 NULL);
1331
1332 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1333 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1334
1335 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1336 char *filename;
1337
1338 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1339 // save_job_file(filename);
1340 g_free(filename);
1341 }
1342 gtk_widget_destroy(dialog);
1343}
1344
Jens Axboe46974a72012-03-02 19:34:13 +01001345static void preferences(GtkWidget *w, gpointer data)
1346{
1347 GtkWidget *dialog, *frame, *box, **buttons;
1348 int i;
1349
1350 dialog = gtk_dialog_new_with_buttons("Preferences",
1351 GTK_WINDOW(ui.window),
1352 GTK_DIALOG_DESTROY_WITH_PARENT,
1353 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1354 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1355 NULL);
1356
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001357 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01001358 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1359 box = gtk_hbox_new(FALSE, 6);
1360 gtk_container_add(GTK_CONTAINER(frame), box);
1361
1362 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1363
1364 for (i = 0; i < FD_DEBUG_MAX; i++) {
1365 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001366 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01001367 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1368 }
1369
1370 gtk_widget_show_all(dialog);
1371
1372 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1373 gtk_widget_destroy(dialog);
1374 return;
1375 }
1376
1377 for (i = 0; i < FD_DEBUG_MAX; i++) {
1378 int set;
1379
1380 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1381 if (set)
1382 fio_debug |= (1UL << i);
1383 }
1384
1385 gtk_widget_destroy(dialog);
1386}
1387
Jens Axboe0420ba62012-02-29 11:16:52 +01001388static void about_dialog(GtkWidget *w, gpointer data)
1389{
1390 gtk_show_about_dialog(NULL,
1391 "program-name", "gfio",
1392 "comments", "Gtk2 UI for fio",
1393 "license", "GPLv2",
1394 "version", fio_version_string,
1395 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1396 "logo-icon-name", "fio",
1397 /* Must be last: */
1398 NULL, NULL,
1399 NULL);
1400}
1401
1402static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001403 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1404 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1405 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1406 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1407 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1408 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1409 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001410};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001411static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001412
1413static const gchar *ui_string = " \
1414 <ui> \
1415 <menubar name=\"MainMenu\"> \
1416 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1417 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1418 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1419 <separator name=\"Separator\"/> \
Jens Axboe46974a72012-03-02 19:34:13 +01001420 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1421 <separator name=\"Separator2\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001422 <menuitem name=\"Quit\" action=\"Quit\" /> \
1423 </menu> \
1424 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1425 <menuitem name=\"About\" action=\"About\" /> \
1426 </menu> \
1427 </menubar> \
1428 </ui> \
1429";
1430
1431static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1432{
1433 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1434 GError *error = 0;
1435
1436 action_group = gtk_action_group_new("Menu");
1437 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1438
1439 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1440 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1441
1442 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1443 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1444}
1445
1446void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1447 GtkWidget *vbox, GtkUIManager *ui_manager)
1448{
1449 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1450}
1451
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001452static void init_ui(int *argc, char **argv[], struct gui *ui)
1453{
Jens Axboe0420ba62012-02-29 11:16:52 +01001454 GtkSettings *settings;
1455 GtkUIManager *uimanager;
Jens Axboe843ad232012-02-29 11:44:53 +01001456 GtkWidget *menu, *probe, *probe_frame, *probe_box;
Jens Axboe0420ba62012-02-29 11:16:52 +01001457
1458 memset(ui, 0, sizeof(*ui));
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001459
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001460 /* Magical g*thread incantation, you just need this thread stuff.
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001461 * Without it, the update that happens in gfio_update_thread_status
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001462 * doesn't really happen in a timely fashion, you need expose events
1463 */
Jens Axboeed727a42012-03-02 12:14:40 +01001464 if (!g_thread_supported())
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001465 g_thread_init(NULL);
1466 gdk_threads_init();
1467
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001468 gtk_init(argc, argv);
Jens Axboe0420ba62012-02-29 11:16:52 +01001469 settings = gtk_settings_get_default();
1470 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1471 g_type_init();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001472
1473 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1474 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1475 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1476
Jens Axboe0420ba62012-02-29 11:16:52 +01001477 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1478 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001479
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001480 ui->vbox = gtk_vbox_new(FALSE, 0);
1481 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001482
Jens Axboe0420ba62012-02-29 11:16:52 +01001483 uimanager = gtk_ui_manager_new();
1484 menu = get_menubar_menu(ui->window, uimanager);
1485 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1486
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001487 /*
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001488 * Set up alignments for widgets at the top of ui,
1489 * align top left, expand horizontally but not vertically
1490 */
1491 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001492 ui->topvbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001493 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001494 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001495
Jens Axboe3e47bd22012-02-29 13:45:02 +01001496 probe = gtk_frame_new("Job");
Jens Axboe843ad232012-02-29 11:44:53 +01001497 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1498 probe_frame = gtk_vbox_new(FALSE, 3);
1499 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1500
1501 probe_box = gtk_hbox_new(FALSE, 3);
1502 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001503 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1504 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1505 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1506 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1507
Jens Axboe3e47bd22012-02-29 13:45:02 +01001508 probe_box = gtk_hbox_new(FALSE, 3);
1509 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001510
Jens Axboeca850992012-03-05 20:04:43 +01001511 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1512 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1513 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1514 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1515 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1516 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001517
1518 probe_box = gtk_hbox_new(FALSE, 3);
1519 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01001520 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1521 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1522 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1523 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001524
1525 /*
1526 * Only add this if we have a commit rate
1527 */
1528#if 0
1529 probe_box = gtk_hbox_new(FALSE, 3);
1530 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1531
Jens Axboe3e47bd22012-02-29 13:45:02 +01001532 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1533 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1534
Jens Axboe3e47bd22012-02-29 13:45:02 +01001535 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1536 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001537#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001538
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001539 /*
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001540 * Add a text box for text op messages
1541 */
1542 ui->textview = gtk_text_view_new();
1543 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1544 gtk_text_buffer_set_text(ui->text, "", -1);
1545 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1546 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1547 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1548 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1549 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1550 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001551 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1552 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001553
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001554 /*
1555 * Set up alignments for widgets at the bottom of ui,
1556 * align bottom left, expand horizontally but not vertically
1557 */
1558 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1559 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1560 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001561 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1562 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001563
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001564 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001565
1566 /*
1567 * Set up thread status progress bar
1568 */
1569 ui->thread_status_pb = gtk_progress_bar_new();
1570 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001572 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1573
1574
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001575 gtk_widget_show_all(ui->window);
1576}
1577
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001578int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001579{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001580 if (initialize_fio(envp))
1581 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001582 if (fio_init_options())
1583 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001584
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001585 init_ui(&argc, &argv, &ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001586
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001587 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001588 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001589 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001590 return 0;
1591}