blob: 256041261f66f0a2c0e565bc4f6eb9b9ba6ed2e8 [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), "");
108 gtk_label_set_text(GTK_LABEL(ui->eta.name), "");
109 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), "");
110 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), "");
111 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), "");
112 gtk_label_set_text(GTK_LABEL(ui->eta.jobs), "");
113 gtk_label_set_text(GTK_LABEL(ui->eta.files), "");
114 gtk_label_set_text(GTK_LABEL(ui->eta.read_bw), "");
115 gtk_label_set_text(GTK_LABEL(ui->eta.read_iops), "");
116 gtk_label_set_text(GTK_LABEL(ui->eta.write_bw), "");
117 gtk_label_set_text(GTK_LABEL(ui->eta.write_iops), "");
118}
119
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
354static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
355 struct thread_stat *ts, int ddir)
356{
357 const char *ddir_label[2] = { "Read", "Write" };
358 GtkWidget *frame, *label, *box, *vbox;
359 unsigned long min, max, runt;
360 unsigned long long bw, iops;
361 double mean, dev;
362 char *io_p, *bw_p, *iops_p;
363 int i2p;
364
365 if (!ts->runtime[ddir])
366 return;
367
368 i2p = is_power_of_2(rs->kb_base);
369 runt = ts->runtime[ddir];
370
371 bw = (1000 * ts->io_bytes[ddir]) / runt;
372 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
373 bw_p = num2str(bw, 6, 1, i2p);
374
375 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
376 iops_p = num2str(iops, 6, 1, 0);
377
378 box = gtk_hbox_new(FALSE, 3);
379 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
380
381 frame = gtk_frame_new(ddir_label[ddir]);
382 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
383
384 vbox = gtk_vbox_new(FALSE, 3);
385 gtk_container_add(GTK_CONTAINER(frame), vbox);
386
387 box = gtk_hbox_new(FALSE, 3);
388 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3);
389
390 label = new_info_label_in_frame(box, "IO");
391 gtk_label_set_text(GTK_LABEL(label), io_p);
392 label = new_info_label_in_frame(box, "Bandwidth");
393 gtk_label_set_text(GTK_LABEL(label), bw_p);
394 label = new_info_label_in_frame(box, "IOPS");
395 gtk_label_set_text(GTK_LABEL(label), iops_p);
396 label = new_info_label_in_frame(box, "Runtime (msec)");
397 label_set_int_value(label, ts->runtime[ddir]);
398
399 frame = gtk_frame_new("Latency");
400 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
401
402 vbox = gtk_vbox_new(FALSE, 3);
403 gtk_container_add(GTK_CONTAINER(frame), vbox);
404
405 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
406 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
407 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
408 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
409 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
410 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
Jens Axboea2697902012-03-05 16:43:49 +0100411 if (ts->clat_percentiles)
412 gfio_show_clat_percentiles(vbox, ts, ddir);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100413
414 free(io_p);
415 free(bw_p);
416 free(iops_p);
417}
418
419static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
420 struct group_run_stats *rs)
421{
422 GtkWidget *dialog, *box, *vbox, *entry, *content;
423 struct gui *ui = client->client_data;
424
425 gdk_threads_enter();
426
427 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
428 GTK_DIALOG_DESTROY_WITH_PARENT,
429 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
430
431 g_signal_connect_swapped(dialog, "response",
432 G_CALLBACK(gtk_widget_destroy),
433 dialog);
434
435 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
436
437 vbox = gtk_vbox_new(FALSE, 3);
438 gtk_container_add(GTK_CONTAINER(content), vbox);
439
440 box = gtk_hbox_new(TRUE, 3);
441 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
442
443 entry = new_info_entry_in_frame(box, "Name");
444 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
445 if (strlen(ts->description)) {
446 entry = new_info_entry_in_frame(box, "Description");
447 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
448 }
449 entry = new_info_entry_in_frame(box, "Group ID");
450 entry_set_int_value(entry, ts->groupid);
451 entry = new_info_entry_in_frame(box, "Jobs");
452 entry_set_int_value(entry, ts->members);
453 entry = new_info_entry_in_frame(box, "Error");
454 entry_set_int_value(entry, ts->error);
455 entry = new_info_entry_in_frame(box, "PID");
456 entry_set_int_value(entry, ts->pid);
457
458 if (ts->io_bytes[DDIR_READ])
459 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
460 if (ts->io_bytes[DDIR_WRITE])
461 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
462
463 gtk_widget_show_all(dialog);
464
465 gdk_threads_leave();
466}
467
Jens Axboe084d1c62012-03-03 20:28:07 +0100468static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100469{
Jens Axboe807f9972012-03-02 10:25:24 +0100470#if 0
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100471 GtkTextBuffer *buffer;
472 GtkTextIter end;
473
474 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
475 gdk_threads_enter();
476 gtk_text_buffer_get_end_iter(buffer, &end);
477 gtk_text_buffer_insert(buffer, &end, buf, -1);
478 gdk_threads_leave();
479 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
480 &end, 0.0, FALSE, 0.0,0.0);
Jens Axboe807f9972012-03-02 10:25:24 +0100481#else
Jens Axboe084d1c62012-03-03 20:28:07 +0100482 fio_client_ops.text_op(client, cmd);
Jens Axboe807f9972012-03-02 10:25:24 +0100483#endif
Stephen M. Camerona1820202012-02-24 08:17:31 +0100484}
485
486static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
487{
488 printf("gfio_disk_util_op called\n");
489 fio_client_ops.disk_util(client, cmd);
490}
491
Jens Axboe3650a3c2012-03-05 14:09:03 +0100492extern int sum_stat_clients;
493extern struct thread_stat client_ts;
494extern struct group_run_stats client_gs;
495
496static int sum_stat_nr;
497
Jens Axboe89e5fad2012-03-05 09:21:12 +0100498static void gfio_thread_status_op(struct fio_client *client,
499 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100500{
Jens Axboe3650a3c2012-03-05 14:09:03 +0100501 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
502
503 gfio_display_ts(client, &p->ts, &p->rs);
504
505 if (sum_stat_clients == 1)
506 return;
507
508 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
509 sum_group_stats(&client_gs, &p->rs);
510
511 client_ts.members++;
512 client_ts.groupid = p->ts.groupid;
513
514 if (++sum_stat_nr == sum_stat_clients) {
515 strcpy(client_ts.name, "All clients");
516 gfio_display_ts(client, &client_ts, &client_gs);
517 }
Stephen M. Camerona1820202012-02-24 08:17:31 +0100518}
519
Jens Axboe89e5fad2012-03-05 09:21:12 +0100520static void gfio_group_stats_op(struct fio_client *client,
521 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100522{
523 printf("gfio_group_stats_op called\n");
Jens Axboe89e5fad2012-03-05 09:21:12 +0100524 fio_client_ops.group_stats(client, cmd);
Stephen M. Camerona1820202012-02-24 08:17:31 +0100525}
526
Jens Axboe3e47bd22012-02-29 13:45:02 +0100527static void gfio_update_eta(struct jobs_eta *je)
528{
529 static int eta_good;
530 char eta_str[128];
531 char output[256];
532 char tmp[32];
533 double perc = 0.0;
534 int i2p = 0;
535
536 eta_str[0] = '\0';
537 output[0] = '\0';
538
539 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
540 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
541 eta_to_str(eta_str, je->eta_sec);
542 }
543
544 sprintf(tmp, "%u", je->nr_running);
545 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
546 sprintf(tmp, "%u", je->files_open);
547 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
548
549#if 0
550 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
551 if (je->m_rate || je->t_rate) {
552 char *tr, *mr;
553
554 mr = num2str(je->m_rate, 4, 0, i2p);
555 tr = num2str(je->t_rate, 4, 0, i2p);
556 gtk_label_set_text(GTK_LABEL(ui.eta.
557 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
558 free(tr);
559 free(mr);
560 } else if (je->m_iops || je->t_iops)
561 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +0100562
Jens Axboe3e47bd22012-02-29 13:45:02 +0100563 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
564 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
565 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
566 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
567#endif
568
569 if (je->eta_sec != INT_MAX && je->nr_running) {
570 char *iops_str[2];
571 char *rate_str[2];
572
573 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
574 strcpy(output, "-.-% done");
575 else {
576 eta_good = 1;
577 perc *= 100.0;
578 sprintf(output, "%3.1f%% done", perc);
579 }
580
581 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
582 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
583
584 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
585 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
586
587 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
588 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
589 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
590 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
591
592 free(rate_str[0]);
593 free(rate_str[1]);
594 free(iops_str[0]);
595 free(iops_str[1]);
596 }
597
598 if (eta_str[0]) {
599 char *dst = output + strlen(output);
600
601 sprintf(dst, " - %s", eta_str);
602 }
603
604 gfio_update_thread_status(output, perc);
605}
606
Stephen M. Camerona1820202012-02-24 08:17:31 +0100607static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
608{
Jens Axboe843ad232012-02-29 11:44:53 +0100609 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
610 const char *os, *arch;
611 char buf[64];
612
613 os = fio_get_os_string(probe->os);
614 if (!os)
615 os = "unknown";
616
617 arch = fio_get_arch_string(probe->arch);
618 if (!arch)
619 os = "unknown";
620
621 if (!client->name)
622 client->name = strdup((char *) probe->hostname);
623
624 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
625 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
626 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
627 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
628 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
Stephen M. Camerona1820202012-02-24 08:17:31 +0100629}
630
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100631static void gfio_update_thread_status(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100632{
633 static char message[100];
634 const char *m = message;
635
636 strncpy(message, status_message, sizeof(message) - 1);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100637 gtk_progress_bar_set_text(
638 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
639 gtk_progress_bar_set_fraction(
640 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100641 gdk_threads_enter();
642 gtk_widget_queue_draw(ui.window);
643 gdk_threads_leave();
644}
645
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100646static void gfio_quit_op(struct fio_client *client)
647{
648 struct gui *ui = client->client_data;
649
650 gfio_set_connected(ui, 0);
651}
652
Jens Axboe807f9972012-03-02 10:25:24 +0100653static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
654{
655 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
656 struct gui *ui = client->client_data;
657 char tmp[8];
658 int i;
659
660 p->iodepth = le32_to_cpu(p->iodepth);
661 p->rw = le32_to_cpu(p->rw);
662
663 for (i = 0; i < 2; i++) {
664 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
665 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
666 }
667
668 p->numjobs = le32_to_cpu(p->numjobs);
669 p->group_reporting = le32_to_cpu(p->group_reporting);
670
671 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
672 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
673 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
674
675 sprintf(tmp, "%u", p->iodepth);
676 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
677}
678
Jens Axboeed727a42012-03-02 12:14:40 +0100679static void gfio_client_timed_out(struct fio_client *client)
680{
681 struct gui *ui = client->client_data;
682 GtkWidget *dialog, *label, *content;
683 char buf[256];
684
685 gdk_threads_enter();
686
687 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +0100688 clear_ui_info(ui);
Jens Axboeed727a42012-03-02 12:14:40 +0100689
690 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
691
692 dialog = gtk_dialog_new_with_buttons("Timed out!",
693 GTK_WINDOW(ui->window),
694 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
695 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
696
697 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
698 label = gtk_label_new((const gchar *) buf);
699 gtk_container_add(GTK_CONTAINER(content), label);
700 gtk_widget_show_all(dialog);
701 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
702
703 gtk_dialog_run(GTK_DIALOG(dialog));
704 gtk_widget_destroy(dialog);
705
706 gdk_threads_leave();
707}
708
Stephen M. Camerona1820202012-02-24 08:17:31 +0100709struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +0100710 .text_op = gfio_text_op,
711 .disk_util = gfio_disk_util_op,
712 .thread_status = gfio_thread_status_op,
713 .group_stats = gfio_group_stats_op,
Jens Axboea5276612012-03-04 15:15:08 +0100714 .eta = gfio_update_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +0100715 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100716 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +0100717 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +0100718 .timed_out = gfio_client_timed_out,
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100719 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +0100720};
721
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100722static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
723 __attribute__((unused)) gpointer data)
724{
725 gtk_main_quit();
726}
727
Stephen M. Cameron25927252012-02-24 08:17:31 +0100728static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100729{
Stephen M. Cameron25927252012-02-24 08:17:31 +0100730 fio_handle_clients(&gfio_client_ops);
Stephen M. Cameron25927252012-02-24 08:17:31 +0100731 return NULL;
732}
733
Jens Axboe0420ba62012-02-29 11:16:52 +0100734static int send_job_files(struct gui *ui)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100735{
Jens Axboe441013b2012-03-01 08:01:52 +0100736 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100737
Jens Axboe0420ba62012-02-29 11:16:52 +0100738 for (i = 0; i < ui->nr_job_files; i++) {
739 ret = fio_clients_send_ini(ui->job_files[i]);
Jens Axboe441013b2012-03-01 08:01:52 +0100740 if (ret)
741 break;
742
Jens Axboe0420ba62012-02-29 11:16:52 +0100743 free(ui->job_files[i]);
744 ui->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +0100745 }
746 while (i < ui->nr_job_files) {
747 free(ui->job_files[i]);
748 ui->job_files[i] = NULL;
749 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +0100750 }
751
Jens Axboe441013b2012-03-01 08:01:52 +0100752 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100753}
754
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100755static void start_job_thread(struct gui *ui)
Stephen M. Cameron25927252012-02-24 08:17:31 +0100756{
Jens Axboe0420ba62012-02-29 11:16:52 +0100757 if (send_job_files(ui)) {
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100758 printf("Yeah, I didn't really like those options too much.\n");
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100759 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
760 return;
761 }
Stephen M. Cameron25927252012-02-24 08:17:31 +0100762}
763
764static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
765 gpointer data)
766{
767 struct gui *ui = data;
768
Stephen M. Cameron25927252012-02-24 08:17:31 +0100769 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100770 start_job_thread(ui);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100771}
772
Jens Axboedf06f222012-03-02 13:32:04 +0100773static void file_open(GtkWidget *w, gpointer data);
774
775static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +0100776{
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100777 struct gui *ui = data;
778
779 if (!ui->connected) {
Jens Axboedf06f222012-03-02 13:32:04 +0100780 if (!ui->nr_job_files)
781 file_open(widget, data);
Jens Axboe8663ea62012-03-02 14:04:30 +0100782 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100783 fio_clients_connect();
784 pthread_create(&ui->t, NULL, job_thread, NULL);
785 gfio_set_connected(ui, 1);
Jens Axboedf06f222012-03-02 13:32:04 +0100786 } else {
787 fio_clients_terminate();
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100788 gfio_set_connected(ui, 0);
Jens Axboe88432652012-03-02 19:09:31 +0100789 clear_ui_info(ui);
Jens Axboedf06f222012-03-02 13:32:04 +0100790 }
Jens Axboe3e47bd22012-02-29 13:45:02 +0100791}
792
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100793static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
794 struct button_spec *buttonspec)
795{
796 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
797 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100798 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100799 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
Jens Axboe3e47bd22012-02-29 13:45:02 +0100800 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100801}
802
803static void add_buttons(struct gui *ui,
804 struct button_spec *buttonlist,
805 int nbuttons)
806{
807 int i;
808
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100809 for (i = 0; i < nbuttons; i++)
810 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
811}
812
Jens Axboe0420ba62012-02-29 11:16:52 +0100813static void on_info_bar_response(GtkWidget *widget, gint response,
814 gpointer data)
815{
816 if (response == GTK_RESPONSE_OK) {
817 gtk_widget_destroy(widget);
818 ui.error_info_bar = NULL;
819 }
820}
821
Jens Axboedf06f222012-03-02 13:32:04 +0100822void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +0100823{
824 if (ui.error_info_bar == NULL) {
825 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
826 GTK_RESPONSE_OK,
827 NULL);
828 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
829 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
830 GTK_MESSAGE_ERROR);
831
832 ui.error_label = gtk_label_new(error->message);
833 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
834 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
835
836 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
837 gtk_widget_show_all(ui.vbox);
838 } else {
839 char buffer[256];
840 snprintf(buffer, sizeof(buffer), "Failed to open file.");
841 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
842 }
843}
844
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100845static int get_connection_details(char **host, int *port, int *type,
846 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +0100847{
848 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100849 GtkWidget *button;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100850 char *typeentry;
851
852 dialog = gtk_dialog_new_with_buttons("Connection details",
853 GTK_WINDOW(ui.window),
854 GTK_DIALOG_DESTROY_WITH_PARENT,
855 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
856 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
857
858 frame = gtk_frame_new("Hostname / socket name");
859 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
860 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
861
862 box = gtk_vbox_new(FALSE, 6);
863 gtk_container_add(GTK_CONTAINER(frame), box);
864
865 hbox = gtk_hbox_new(TRUE, 10);
866 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
867 hentry = gtk_entry_new();
868 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
869 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
870
871 frame = gtk_frame_new("Port");
872 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
873 box = gtk_vbox_new(FALSE, 10);
874 gtk_container_add(GTK_CONTAINER(frame), box);
875
876 hbox = gtk_hbox_new(TRUE, 4);
877 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
878 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
879
880 frame = gtk_frame_new("Type");
881 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
882 box = gtk_vbox_new(FALSE, 10);
883 gtk_container_add(GTK_CONTAINER(frame), box);
884
885 hbox = gtk_hbox_new(TRUE, 4);
886 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
887
888 combo = gtk_combo_box_text_new();
889 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
890 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
891 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
892 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
893
894 gtk_container_add(GTK_CONTAINER(hbox), combo);
895
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100896 frame = gtk_frame_new("Options");
897 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
898 box = gtk_vbox_new(FALSE, 10);
899 gtk_container_add(GTK_CONTAINER(frame), box);
900
901 hbox = gtk_hbox_new(TRUE, 4);
902 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
903
904 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
905 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
906 gtk_widget_set_tooltip_text(button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
907 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
908
Jens Axboea7a42ce2012-03-02 13:12:04 +0100909 gtk_widget_show_all(dialog);
910
911 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
912 gtk_widget_destroy(dialog);
913 return 1;
914 }
915
916 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
917 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
918
919 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
920 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
921 *type = Fio_client_ipv4;
922 else if (!strncmp(typeentry, "IPv6", 4))
923 *type = Fio_client_ipv6;
924 else
925 *type = Fio_client_socket;
926 g_free(typeentry);
927
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100928 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
929
Jens Axboea7a42ce2012-03-02 13:12:04 +0100930 gtk_widget_destroy(dialog);
931 return 0;
932}
933
Jens Axboe0420ba62012-02-29 11:16:52 +0100934static void file_open(GtkWidget *w, gpointer data)
935{
936 GtkWidget *dialog;
937 GSList *filenames, *fn_glist;
938 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100939 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100940 int port, type, server_start;
Jens Axboe0420ba62012-02-29 11:16:52 +0100941
942 dialog = gtk_file_chooser_dialog_new("Open File",
943 GTK_WINDOW(ui.window),
944 GTK_FILE_CHOOSER_ACTION_OPEN,
945 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
946 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
947 NULL);
948 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
949
950 filter = gtk_file_filter_new();
951 gtk_file_filter_add_pattern(filter, "*.fio");
952 gtk_file_filter_add_pattern(filter, "*.job");
953 gtk_file_filter_add_mime_type(filter, "text/fio");
954 gtk_file_filter_set_name(filter, "Fio job file");
955 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
956
957 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
958 gtk_widget_destroy(dialog);
959 return;
960 }
961
962 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +0100963
964 gtk_widget_destroy(dialog);
965
Jens Axboeb9f3c7e2012-03-02 14:27:17 +0100966 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +0100967 goto err;
968
Jens Axboe0420ba62012-02-29 11:16:52 +0100969 filenames = fn_glist;
970 while (filenames != NULL) {
Jens Axboe0420ba62012-02-29 11:16:52 +0100971 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
972 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
973 ui.nr_job_files++;
974
Jens Axboea5276612012-03-04 15:15:08 +0100975 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
Jens Axboedf06f222012-03-02 13:32:04 +0100976 if (!ui.client) {
977 GError *error;
978
979 error = g_error_new(g_quark_from_string("fio"), 1,
980 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +0100981 report_error(error);
982 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +0100983 }
Jens Axboedf06f222012-03-02 13:32:04 +0100984 ui.client->client_data = &ui;
Jens Axboe0420ba62012-02-29 11:16:52 +0100985
986 g_free(filenames->data);
987 filenames = g_slist_next(filenames);
988 }
Jens Axboea7a42ce2012-03-02 13:12:04 +0100989 free(host);
990err:
Jens Axboe0420ba62012-02-29 11:16:52 +0100991 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +0100992}
993
994static void file_save(GtkWidget *w, gpointer data)
995{
996 GtkWidget *dialog;
997
998 dialog = gtk_file_chooser_dialog_new("Save File",
999 GTK_WINDOW(ui.window),
1000 GTK_FILE_CHOOSER_ACTION_SAVE,
1001 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1002 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1003 NULL);
1004
1005 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1006 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1007
1008 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1009 char *filename;
1010
1011 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1012 // save_job_file(filename);
1013 g_free(filename);
1014 }
1015 gtk_widget_destroy(dialog);
1016}
1017
Jens Axboe46974a72012-03-02 19:34:13 +01001018static void preferences(GtkWidget *w, gpointer data)
1019{
1020 GtkWidget *dialog, *frame, *box, **buttons;
1021 int i;
1022
1023 dialog = gtk_dialog_new_with_buttons("Preferences",
1024 GTK_WINDOW(ui.window),
1025 GTK_DIALOG_DESTROY_WITH_PARENT,
1026 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1027 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1028 NULL);
1029
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001030 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01001031 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1032 box = gtk_hbox_new(FALSE, 6);
1033 gtk_container_add(GTK_CONTAINER(frame), box);
1034
1035 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1036
1037 for (i = 0; i < FD_DEBUG_MAX; i++) {
1038 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001039 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01001040 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1041 }
1042
1043 gtk_widget_show_all(dialog);
1044
1045 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1046 gtk_widget_destroy(dialog);
1047 return;
1048 }
1049
1050 for (i = 0; i < FD_DEBUG_MAX; i++) {
1051 int set;
1052
1053 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1054 if (set)
1055 fio_debug |= (1UL << i);
1056 }
1057
1058 gtk_widget_destroy(dialog);
1059}
1060
Jens Axboe0420ba62012-02-29 11:16:52 +01001061static void about_dialog(GtkWidget *w, gpointer data)
1062{
1063 gtk_show_about_dialog(NULL,
1064 "program-name", "gfio",
1065 "comments", "Gtk2 UI for fio",
1066 "license", "GPLv2",
1067 "version", fio_version_string,
1068 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1069 "logo-icon-name", "fio",
1070 /* Must be last: */
1071 NULL, NULL,
1072 NULL);
1073}
1074
1075static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001076 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1077 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1078 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1079 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1080 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1081 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1082 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001083};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001084static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001085
1086static const gchar *ui_string = " \
1087 <ui> \
1088 <menubar name=\"MainMenu\"> \
1089 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1090 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1091 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1092 <separator name=\"Separator\"/> \
Jens Axboe46974a72012-03-02 19:34:13 +01001093 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1094 <separator name=\"Separator2\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001095 <menuitem name=\"Quit\" action=\"Quit\" /> \
1096 </menu> \
1097 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1098 <menuitem name=\"About\" action=\"About\" /> \
1099 </menu> \
1100 </menubar> \
1101 </ui> \
1102";
1103
1104static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1105{
1106 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1107 GError *error = 0;
1108
1109 action_group = gtk_action_group_new("Menu");
1110 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1111
1112 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1113 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1114
1115 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1116 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1117}
1118
1119void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1120 GtkWidget *vbox, GtkUIManager *ui_manager)
1121{
1122 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1123}
1124
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001125static void init_ui(int *argc, char **argv[], struct gui *ui)
1126{
Jens Axboe0420ba62012-02-29 11:16:52 +01001127 GtkSettings *settings;
1128 GtkUIManager *uimanager;
Jens Axboe843ad232012-02-29 11:44:53 +01001129 GtkWidget *menu, *probe, *probe_frame, *probe_box;
Jens Axboe0420ba62012-02-29 11:16:52 +01001130
1131 memset(ui, 0, sizeof(*ui));
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001132
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001133 /* Magical g*thread incantation, you just need this thread stuff.
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001134 * Without it, the update that happens in gfio_update_thread_status
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001135 * doesn't really happen in a timely fashion, you need expose events
1136 */
Jens Axboeed727a42012-03-02 12:14:40 +01001137 if (!g_thread_supported())
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001138 g_thread_init(NULL);
1139 gdk_threads_init();
1140
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001141 gtk_init(argc, argv);
Jens Axboe0420ba62012-02-29 11:16:52 +01001142 settings = gtk_settings_get_default();
1143 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1144 g_type_init();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001145
1146 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1147 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1148 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1149
Jens Axboe0420ba62012-02-29 11:16:52 +01001150 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1151 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001152
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001153 ui->vbox = gtk_vbox_new(FALSE, 0);
1154 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001155
Jens Axboe0420ba62012-02-29 11:16:52 +01001156 uimanager = gtk_ui_manager_new();
1157 menu = get_menubar_menu(ui->window, uimanager);
1158 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1159
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001160 /*
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001161 * Set up alignments for widgets at the top of ui,
1162 * align top left, expand horizontally but not vertically
1163 */
1164 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001165 ui->topvbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001166 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001167 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001168
Jens Axboe3e47bd22012-02-29 13:45:02 +01001169 probe = gtk_frame_new("Job");
Jens Axboe843ad232012-02-29 11:44:53 +01001170 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1171 probe_frame = gtk_vbox_new(FALSE, 3);
1172 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1173
1174 probe_box = gtk_hbox_new(FALSE, 3);
1175 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001176 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1177 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1178 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1179 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1180
Jens Axboe3e47bd22012-02-29 13:45:02 +01001181 probe_box = gtk_hbox_new(FALSE, 3);
1182 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001183
1184 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
1185 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
1186 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
1187 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001188 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
1189 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
1190
1191 probe_box = gtk_hbox_new(FALSE, 3);
1192 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1193 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
1194 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001195 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
1196 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
1197
1198 /*
1199 * Only add this if we have a commit rate
1200 */
1201#if 0
1202 probe_box = gtk_hbox_new(FALSE, 3);
1203 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1204
Jens Axboe3e47bd22012-02-29 13:45:02 +01001205 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1206 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1207
Jens Axboe3e47bd22012-02-29 13:45:02 +01001208 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1209 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001210#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001211
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001212 /*
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001213 * Add a text box for text op messages
1214 */
1215 ui->textview = gtk_text_view_new();
1216 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1217 gtk_text_buffer_set_text(ui->text, "", -1);
1218 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1219 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1220 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1221 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1222 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1223 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001224 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1225 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001226
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001227 /*
1228 * Set up alignments for widgets at the bottom of ui,
1229 * align bottom left, expand horizontally but not vertically
1230 */
1231 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1232 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1233 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Stephen M. Camerone1645342012-02-24 08:17:32 +01001234 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1235 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001236
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001237 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001238
1239 /*
1240 * Set up thread status progress bar
1241 */
1242 ui->thread_status_pb = gtk_progress_bar_new();
1243 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001244 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001245 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1246
1247
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001248 gtk_widget_show_all(ui->window);
1249}
1250
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001251int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001252{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001253 if (initialize_fio(envp))
1254 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001255 if (fio_init_options())
1256 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001257
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001258 init_ui(&argc, &argv, &ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001259
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001260 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001261 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001262 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001263 return 0;
1264}