gfio.c revision 9988ca70151a191c477179be7999300ddff16230
1/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
6 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 2 as
12 *  published by the Free Software Foundation.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 */
24#include <locale.h>
25#include <malloc.h>
26#include <string.h>
27
28#include <glib.h>
29#include <cairo.h>
30#include <gtk/gtk.h>
31
32#include "fio.h"
33#include "graph.h"
34
35static int gfio_server_running;
36static const char *gfio_graph_font;
37
38static void view_log(GtkWidget *w, gpointer data);
39
40#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
41
42typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
43
44static void connect_clicked(GtkWidget *widget, gpointer data);
45static void start_job_clicked(GtkWidget *widget, gpointer data);
46static void send_clicked(GtkWidget *widget, gpointer data);
47
48static struct button_spec {
49	const char *buttontext;
50	clickfunction f;
51	const char *tooltiptext;
52	const int start_insensitive;
53} buttonspeclist[] = {
54#define CONNECT_BUTTON 0
55#define SEND_BUTTON 1
56#define START_JOB_BUTTON 2
57	{ "Connect", connect_clicked, "Connect to host", 0 },
58	{ "Send", send_clicked, "Send job description to host", 1 },
59	{ "Start Job", start_job_clicked,
60		"Start the current job on the server", 1 },
61};
62
63struct probe_widget {
64	GtkWidget *hostname;
65	GtkWidget *os;
66	GtkWidget *arch;
67	GtkWidget *fio_ver;
68};
69
70struct eta_widget {
71	GtkWidget *name;
72	GtkWidget *iotype;
73	GtkWidget *ioengine;
74	GtkWidget *iodepth;
75	GtkWidget *jobs;
76	GtkWidget *files;
77	GtkWidget *read_bw;
78	GtkWidget *read_iops;
79	GtkWidget *cr_bw;
80	GtkWidget *cr_iops;
81	GtkWidget *write_bw;
82	GtkWidget *write_iops;
83	GtkWidget *cw_bw;
84	GtkWidget *cw_iops;
85};
86
87struct gfio_graphs {
88#define DRAWING_AREA_XDIM 1000
89#define DRAWING_AREA_YDIM 400
90	GtkWidget *drawing_area;
91	int drawing_area_xdim;
92	int drawing_area_ydim;
93
94	struct graph *iops_graph;
95	struct graph *bandwidth_graph;
96};
97
98/*
99 * Main window widgets and data
100 */
101struct gui {
102	GtkWidget *window;
103	GtkWidget *vbox;
104	GtkWidget *topvbox;
105	GtkWidget *topalign;
106	GtkWidget *bottomalign;
107	GtkWidget *thread_status_pb;
108	GtkWidget *buttonbox;
109	GtkWidget *scrolled_window;
110	GtkWidget *notebook;
111	GtkWidget *error_info_bar;
112	GtkWidget *error_label;
113	GtkListStore *log_model;
114	GtkWidget *log_tree;
115	GtkWidget *log_view;
116	struct gfio_graphs graphs;
117	struct probe_widget probe;
118	struct eta_widget eta;
119	pthread_t server_t;
120
121	pthread_t t;
122	int handler_running;
123
124	struct flist_head list;
125} main_ui;
126
127/*
128 * Notebook entry
129 */
130struct gui_entry {
131	struct flist_head list;
132	struct gui *ui;
133
134	GtkWidget *vbox;
135	GtkWidget *topvbox;
136	GtkWidget *topalign;
137	GtkWidget *bottomalign;
138	GtkWidget *thread_status_pb;
139	GtkWidget *buttonbox;
140	GtkWidget *button[ARRAYSIZE(buttonspeclist)];
141	GtkWidget *scrolled_window;
142	GtkWidget *notebook;
143	GtkWidget *error_info_bar;
144	GtkWidget *error_label;
145	GtkWidget *results_notebook;
146	GtkWidget *results_window;
147	GtkListStore *log_model;
148	GtkWidget *log_tree;
149	GtkWidget *log_view;
150	struct gfio_graphs graphs;
151	struct probe_widget probe;
152	struct eta_widget eta;
153	GtkWidget *page_label;
154	gint page_num;
155	int connected;
156
157	struct gfio_client *client;
158	int nr_job_files;
159	char **job_files;
160};
161
162struct gfio_client {
163	struct gui_entry *ge;
164	struct fio_client *client;
165	GtkWidget *results_widget;
166	GtkWidget *disk_util_frame;
167	GtkWidget *err_entry;
168	unsigned int job_added;
169	struct thread_options o;
170};
171
172static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
173static void gfio_update_thread_status_all(char *status_message, double perc);
174
175static struct graph *setup_iops_graph(void)
176{
177	struct graph *g;
178
179	g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
180	graph_title(g, "IOPS");
181	graph_x_title(g, "Time (secs)");
182	graph_y_title(g, "IOs / sec");
183	graph_add_label(g, "Read IOPS");
184	graph_add_label(g, "Write IOPS");
185	graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
186	graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
187	line_graph_set_data_count_limit(g, 100);
188	return g;
189}
190
191static struct graph *setup_bandwidth_graph(void)
192{
193	struct graph *g;
194
195	g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
196	graph_title(g, "Bandwidth");
197	graph_x_title(g, "Time (secs)");
198	graph_y_title(g, "Kbytes / sec");
199	graph_add_label(g, "Read Bandwidth");
200	graph_add_label(g, "Write Bandwidth");
201	graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
202	graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
203	line_graph_set_data_count_limit(g, 100);
204	return g;
205}
206
207static void setup_graphs(struct gfio_graphs *g)
208{
209	g->iops_graph = setup_iops_graph();
210	g->bandwidth_graph = setup_bandwidth_graph();
211}
212
213static void clear_ge_ui_info(struct gui_entry *ge)
214{
215	gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
216	gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
217	gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
218	gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
219	gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
220	gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
221	gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
222	gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
223	gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
224	gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
225	gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
226	gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
227	gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
228	gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
229}
230
231static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
232{
233	GtkWidget *entry, *frame;
234
235	frame = gtk_frame_new(label);
236	entry = gtk_entry_new();
237	gtk_entry_set_editable(GTK_ENTRY(entry), 0);
238	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
239	gtk_container_add(GTK_CONTAINER(frame), entry);
240
241	return entry;
242}
243
244static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
245{
246	GtkWidget *label_widget;
247	GtkWidget *frame;
248
249	frame = gtk_frame_new(label);
250	label_widget = gtk_label_new(NULL);
251	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
252	gtk_container_add(GTK_CONTAINER(frame), label_widget);
253
254	return label_widget;
255}
256
257static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
258{
259	GtkWidget *button, *box;
260
261	box = gtk_hbox_new(FALSE, 3);
262	gtk_container_add(GTK_CONTAINER(hbox), box);
263
264	button = gtk_spin_button_new_with_range(min, max, 1.0);
265	gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
266
267	gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
268	gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
269
270	return button;
271}
272
273static void gfio_set_connected(struct gui_entry *ge, int connected)
274{
275	if (connected) {
276		gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
277		ge->connected = 1;
278		gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
279		gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
280	} else {
281		ge->connected = 0;
282		gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
283		gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
284		gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
285		gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
286	}
287}
288
289static void label_set_int_value(GtkWidget *entry, unsigned int val)
290{
291	char tmp[80];
292
293	sprintf(tmp, "%u", val);
294	gtk_label_set_text(GTK_LABEL(entry), tmp);
295}
296
297static void entry_set_int_value(GtkWidget *entry, unsigned int val)
298{
299	char tmp[80];
300
301	sprintf(tmp, "%u", val);
302	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
303}
304
305#define ALIGN_LEFT 1
306#define ALIGN_RIGHT 2
307#define INVISIBLE 4
308#define UNSORTABLE 8
309
310GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
311{
312	GtkCellRenderer *renderer;
313	GtkTreeViewColumn *col;
314	double xalign = 0.0; /* left as default */
315	PangoAlignment align;
316	gboolean visible;
317
318	align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
319		(flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
320		PANGO_ALIGN_CENTER;
321	visible = !(flags & INVISIBLE);
322
323	renderer = gtk_cell_renderer_text_new();
324	col = gtk_tree_view_column_new();
325
326	gtk_tree_view_column_set_title(col, title);
327	if (!(flags & UNSORTABLE))
328		gtk_tree_view_column_set_sort_column_id(col, index);
329	gtk_tree_view_column_set_resizable(col, TRUE);
330	gtk_tree_view_column_pack_start(col, renderer, TRUE);
331	gtk_tree_view_column_add_attribute(col, renderer, "text", index);
332	gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
333	switch (align) {
334	case PANGO_ALIGN_LEFT:
335		xalign = 0.0;
336		break;
337	case PANGO_ALIGN_CENTER:
338		xalign = 0.5;
339		break;
340	case PANGO_ALIGN_RIGHT:
341		xalign = 1.0;
342		break;
343	}
344	gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
345	gtk_tree_view_column_set_visible(col, visible);
346	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
347	return col;
348}
349
350static void gfio_ui_setup_log(struct gui *ui)
351{
352	GtkTreeSelection *selection;
353	GtkListStore *model;
354	GtkWidget *tree_view;
355
356	model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
357
358	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
359	gtk_widget_set_can_focus(tree_view, FALSE);
360
361	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
362	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
363	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
364		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
365
366	tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
367	tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
368	tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
369	tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
370
371	ui->log_model = model;
372	ui->log_tree = tree_view;
373}
374
375static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
376					       fio_fp64_t *plist,
377					       unsigned int len,
378					       const char *base,
379					       unsigned int scale)
380{
381	GType types[FIO_IO_U_LIST_MAX_LEN];
382	GtkWidget *tree_view;
383	GtkTreeSelection *selection;
384	GtkListStore *model;
385	GtkTreeIter iter;
386	int i;
387
388	for (i = 0; i < len; i++)
389		types[i] = G_TYPE_INT;
390
391	model = gtk_list_store_newv(len, types);
392
393	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
394	gtk_widget_set_can_focus(tree_view, FALSE);
395
396	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
397		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
398
399	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
400	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
401
402	for (i = 0; i < len; i++) {
403		char fbuf[8];
404
405		sprintf(fbuf, "%2.2f%%", plist[i].u.f);
406		tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
407	}
408
409	gtk_list_store_append(model, &iter);
410
411	for (i = 0; i < len; i++) {
412		if (scale)
413			ovals[i] = (ovals[i] + 999) / 1000;
414		gtk_list_store_set(model, &iter, i, ovals[i], -1);
415	}
416
417	return tree_view;
418}
419
420static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
421				       int ddir)
422{
423	unsigned int *io_u_plat = ts->io_u_plat[ddir];
424	unsigned long nr = ts->clat_stat[ddir].samples;
425	fio_fp64_t *plist = ts->percentile_list;
426	unsigned int *ovals, len, minv, maxv, scale_down;
427	const char *base;
428	GtkWidget *tree_view, *frame, *hbox;
429	char tmp[64];
430
431	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
432	if (!len)
433		goto out;
434
435	/*
436	 * We default to usecs, but if the value range is such that we
437	 * should scale down to msecs, do that.
438	 */
439	if (minv > 2000 && maxv > 99999) {
440		scale_down = 1;
441		base = "msec";
442	} else {
443		scale_down = 0;
444		base = "usec";
445	}
446
447	tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
448
449	sprintf(tmp, "Completion percentiles (%s)", base);
450	frame = gtk_frame_new(tmp);
451	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
452
453	hbox = gtk_hbox_new(FALSE, 3);
454	gtk_container_add(GTK_CONTAINER(frame), hbox);
455
456	gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
457out:
458	if (ovals)
459		free(ovals);
460}
461
462static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
463			  unsigned long max, double mean, double dev)
464{
465	const char *base = "(usec)";
466	GtkWidget *hbox, *label, *frame;
467	char *minp, *maxp;
468	char tmp[64];
469
470	if (!usec_to_msec(&min, &max, &mean, &dev))
471		base = "(msec)";
472
473	minp = num2str(min, 6, 1, 0);
474	maxp = num2str(max, 6, 1, 0);
475
476	sprintf(tmp, "%s %s", name, base);
477	frame = gtk_frame_new(tmp);
478	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
479
480	hbox = gtk_hbox_new(FALSE, 3);
481	gtk_container_add(GTK_CONTAINER(frame), hbox);
482
483	label = new_info_label_in_frame(hbox, "Minimum");
484	gtk_label_set_text(GTK_LABEL(label), minp);
485	label = new_info_label_in_frame(hbox, "Maximum");
486	gtk_label_set_text(GTK_LABEL(label), maxp);
487	label = new_info_label_in_frame(hbox, "Average");
488	sprintf(tmp, "%5.02f", mean);
489	gtk_label_set_text(GTK_LABEL(label), tmp);
490	label = new_info_label_in_frame(hbox, "Standard deviation");
491	sprintf(tmp, "%5.02f", dev);
492	gtk_label_set_text(GTK_LABEL(label), tmp);
493
494	free(minp);
495	free(maxp);
496
497}
498
499#define GFIO_CLAT	1
500#define GFIO_SLAT	2
501#define GFIO_LAT	4
502
503static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
504				  struct thread_stat *ts, int ddir)
505{
506	const char *ddir_label[2] = { "Read", "Write" };
507	GtkWidget *frame, *label, *box, *vbox, *main_vbox;
508	unsigned long min[3], max[3], runt;
509	unsigned long long bw, iops;
510	unsigned int flags = 0;
511	double mean[3], dev[3];
512	char *io_p, *bw_p, *iops_p;
513	int i2p;
514
515	if (!ts->runtime[ddir])
516		return;
517
518	i2p = is_power_of_2(rs->kb_base);
519	runt = ts->runtime[ddir];
520
521	bw = (1000 * ts->io_bytes[ddir]) / runt;
522	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
523	bw_p = num2str(bw, 6, 1, i2p);
524
525	iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
526	iops_p = num2str(iops, 6, 1, 0);
527
528	box = gtk_hbox_new(FALSE, 3);
529	gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
530
531	frame = gtk_frame_new(ddir_label[ddir]);
532	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
533
534	main_vbox = gtk_vbox_new(FALSE, 3);
535	gtk_container_add(GTK_CONTAINER(frame), main_vbox);
536
537	box = gtk_hbox_new(FALSE, 3);
538	gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
539
540	label = new_info_label_in_frame(box, "IO");
541	gtk_label_set_text(GTK_LABEL(label), io_p);
542	label = new_info_label_in_frame(box, "Bandwidth");
543	gtk_label_set_text(GTK_LABEL(label), bw_p);
544	label = new_info_label_in_frame(box, "IOPS");
545	gtk_label_set_text(GTK_LABEL(label), iops_p);
546	label = new_info_label_in_frame(box, "Runtime (msec)");
547	label_set_int_value(label, ts->runtime[ddir]);
548
549	if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
550		double p_of_agg = 100.0;
551		const char *bw_str = "KB";
552		char tmp[32];
553
554		if (rs->agg[ddir]) {
555			p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
556			if (p_of_agg > 100.0)
557				p_of_agg = 100.0;
558		}
559
560		if (mean[0] > 999999.9) {
561			min[0] /= 1000.0;
562			max[0] /= 1000.0;
563			mean[0] /= 1000.0;
564			dev[0] /= 1000.0;
565			bw_str = "MB";
566		}
567
568		sprintf(tmp, "Bandwidth (%s)", bw_str);
569		frame = gtk_frame_new(tmp);
570		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
571
572		box = gtk_hbox_new(FALSE, 3);
573		gtk_container_add(GTK_CONTAINER(frame), box);
574
575		label = new_info_label_in_frame(box, "Minimum");
576		label_set_int_value(label, min[0]);
577		label = new_info_label_in_frame(box, "Maximum");
578		label_set_int_value(label, max[0]);
579		label = new_info_label_in_frame(box, "Percentage of jobs");
580		sprintf(tmp, "%3.2f%%", p_of_agg);
581		gtk_label_set_text(GTK_LABEL(label), tmp);
582		label = new_info_label_in_frame(box, "Average");
583		sprintf(tmp, "%5.02f", mean[0]);
584		gtk_label_set_text(GTK_LABEL(label), tmp);
585		label = new_info_label_in_frame(box, "Standard deviation");
586		sprintf(tmp, "%5.02f", dev[0]);
587		gtk_label_set_text(GTK_LABEL(label), tmp);
588	}
589
590	if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
591		flags |= GFIO_SLAT;
592	if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
593		flags |= GFIO_CLAT;
594	if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
595		flags |= GFIO_LAT;
596
597	if (flags) {
598		frame = gtk_frame_new("Latency");
599		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
600
601		vbox = gtk_vbox_new(FALSE, 3);
602		gtk_container_add(GTK_CONTAINER(frame), vbox);
603
604		if (flags & GFIO_SLAT)
605			gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
606		if (flags & GFIO_CLAT)
607			gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
608		if (flags & GFIO_LAT)
609			gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
610	}
611
612	if (ts->clat_percentiles)
613		gfio_show_clat_percentiles(main_vbox, ts, ddir);
614
615
616	free(io_p);
617	free(bw_p);
618	free(iops_p);
619}
620
621static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
622					  const char **labels)
623{
624	GtkWidget *tree_view;
625	GtkTreeSelection *selection;
626	GtkListStore *model;
627	GtkTreeIter iter;
628	GType *types;
629	int i, skipped;
630
631	/*
632	 * Check if all are empty, in which case don't bother
633	 */
634	for (i = 0, skipped = 0; i < num; i++)
635		if (lat[i] <= 0.0)
636			skipped++;
637
638	if (skipped == num)
639		return NULL;
640
641	types = malloc(num * sizeof(GType));
642
643	for (i = 0; i < num; i++)
644		types[i] = G_TYPE_STRING;
645
646	model = gtk_list_store_newv(num, types);
647	free(types);
648	types = NULL;
649
650	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
651	gtk_widget_set_can_focus(tree_view, FALSE);
652
653	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
654		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
655
656	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
657	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
658
659	for (i = 0; i < num; i++)
660		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
661
662	gtk_list_store_append(model, &iter);
663
664	for (i = 0; i < num; i++) {
665		char fbuf[32];
666
667		if (lat[i] <= 0.0)
668			sprintf(fbuf, "0.00");
669		else
670			sprintf(fbuf, "%3.2f%%", lat[i]);
671
672		gtk_list_store_set(model, &iter, i, fbuf, -1);
673	}
674
675	return tree_view;
676}
677
678static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
679{
680	GtkWidget *box, *frame, *tree_view;
681	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
682	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
683	const char *uranges[] = { "2", "4", "10", "20", "50", "100",
684				  "250", "500", "750", "1000", };
685	const char *mranges[] = { "2", "4", "10", "20", "50", "100",
686				  "250", "500", "750", "1000", "2000",
687				  ">= 2000", };
688
689	stat_calc_lat_u(ts, io_u_lat_u);
690	stat_calc_lat_m(ts, io_u_lat_m);
691
692	tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
693	if (tree_view) {
694		frame = gtk_frame_new("Latency buckets (usec)");
695		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
696
697		box = gtk_hbox_new(FALSE, 3);
698		gtk_container_add(GTK_CONTAINER(frame), box);
699		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
700	}
701
702	tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
703	if (tree_view) {
704		frame = gtk_frame_new("Latency buckets (msec)");
705		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
706
707		box = gtk_hbox_new(FALSE, 3);
708		gtk_container_add(GTK_CONTAINER(frame), box);
709		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
710	}
711}
712
713static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
714{
715	GtkWidget *box, *frame, *entry;
716	double usr_cpu, sys_cpu;
717	unsigned long runtime;
718	char tmp[32];
719
720	runtime = ts->total_run_time;
721	if (runtime) {
722		double runt = (double) runtime;
723
724		usr_cpu = (double) ts->usr_time * 100 / runt;
725		sys_cpu = (double) ts->sys_time * 100 / runt;
726	} else {
727		usr_cpu = 0;
728		sys_cpu = 0;
729	}
730
731	frame = gtk_frame_new("OS resources");
732	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
733
734	box = gtk_hbox_new(FALSE, 3);
735	gtk_container_add(GTK_CONTAINER(frame), box);
736
737	entry = new_info_entry_in_frame(box, "User CPU");
738	sprintf(tmp, "%3.2f%%", usr_cpu);
739	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
740	entry = new_info_entry_in_frame(box, "System CPU");
741	sprintf(tmp, "%3.2f%%", sys_cpu);
742	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
743	entry = new_info_entry_in_frame(box, "Context switches");
744	entry_set_int_value(entry, ts->ctx);
745	entry = new_info_entry_in_frame(box, "Major faults");
746	entry_set_int_value(entry, ts->majf);
747	entry = new_info_entry_in_frame(box, "Minor faults");
748	entry_set_int_value(entry, ts->minf);
749}
750static void gfio_add_sc_depths_tree(GtkListStore *model,
751				    struct thread_stat *ts, unsigned int len,
752				    int submit)
753{
754	double io_u_dist[FIO_IO_U_MAP_NR];
755	GtkTreeIter iter;
756	/* Bits 0, and 3-8 */
757	const int add_mask = 0x1f9;
758	int i, j;
759
760	if (submit)
761		stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
762	else
763		stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
764
765	gtk_list_store_append(model, &iter);
766
767	gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
768
769	for (i = 1, j = 0; i < len; i++) {
770		char fbuf[32];
771
772		if (!(add_mask & (1UL << (i - 1))))
773			sprintf(fbuf, "0.0%%");
774		else {
775			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
776			j++;
777		}
778
779		gtk_list_store_set(model, &iter, i, fbuf, -1);
780	}
781
782}
783
784static void gfio_add_total_depths_tree(GtkListStore *model,
785				       struct thread_stat *ts, unsigned int len)
786{
787	double io_u_dist[FIO_IO_U_MAP_NR];
788	GtkTreeIter iter;
789	/* Bits 1-6, and 8 */
790	const int add_mask = 0x17e;
791	int i, j;
792
793	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
794
795	gtk_list_store_append(model, &iter);
796
797	gtk_list_store_set(model, &iter, 0, "Total", -1);
798
799	for (i = 1, j = 0; i < len; i++) {
800		char fbuf[32];
801
802		if (!(add_mask & (1UL << (i - 1))))
803			sprintf(fbuf, "0.0%%");
804		else {
805			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
806			j++;
807		}
808
809		gtk_list_store_set(model, &iter, i, fbuf, -1);
810	}
811
812}
813
814static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
815{
816	GtkWidget *frame, *box, *tree_view;
817	GtkTreeSelection *selection;
818	GtkListStore *model;
819	GType types[FIO_IO_U_MAP_NR + 1];
820	int i;
821#define NR_LABELS	10
822	const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
823
824	frame = gtk_frame_new("IO depths");
825	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
826
827	box = gtk_hbox_new(FALSE, 3);
828	gtk_container_add(GTK_CONTAINER(frame), box);
829
830	for (i = 0; i < NR_LABELS; i++)
831		types[i] = G_TYPE_STRING;
832
833	model = gtk_list_store_newv(NR_LABELS, types);
834
835	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
836	gtk_widget_set_can_focus(tree_view, FALSE);
837
838	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
839		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
840
841	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
842	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
843
844	for (i = 0; i < NR_LABELS; i++)
845		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
846
847	gfio_add_total_depths_tree(model, ts, NR_LABELS);
848	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
849	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
850
851	gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
852}
853
854static gboolean results_window_delete(GtkWidget *w, gpointer data)
855{
856	struct gui_entry *ge = (struct gui_entry *) data;
857
858	gtk_widget_destroy(w);
859	ge->results_window = NULL;
860	ge->results_notebook = NULL;
861	return TRUE;
862}
863
864static GtkWidget *get_results_window(struct gui_entry *ge)
865{
866	GtkWidget *win, *notebook;
867
868	if (ge->results_window)
869		return ge->results_notebook;
870
871	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
872	gtk_window_set_title(GTK_WINDOW(win), "Results");
873	gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
874	g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
875	g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
876
877	notebook = gtk_notebook_new();
878	gtk_container_add(GTK_CONTAINER(win), notebook);
879
880	ge->results_window = win;
881	ge->results_notebook = notebook;
882	return ge->results_notebook;
883}
884
885static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
886			    struct group_run_stats *rs)
887{
888	GtkWidget *res_win, *box, *vbox, *entry, *scroll;
889	struct gfio_client *gc = client->client_data;
890
891	gdk_threads_enter();
892
893	res_win = get_results_window(gc->ge);
894
895	scroll = gtk_scrolled_window_new(NULL, NULL);
896	gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
897	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
898
899	vbox = gtk_vbox_new(FALSE, 3);
900
901	box = gtk_hbox_new(FALSE, 0);
902	gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
903
904	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
905
906	gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
907
908	gc->results_widget = vbox;
909
910	entry = new_info_entry_in_frame(box, "Name");
911	gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
912	if (strlen(ts->description)) {
913		entry = new_info_entry_in_frame(box, "Description");
914		gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
915	}
916	entry = new_info_entry_in_frame(box, "Group ID");
917	entry_set_int_value(entry, ts->groupid);
918	entry = new_info_entry_in_frame(box, "Jobs");
919	entry_set_int_value(entry, ts->members);
920	gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
921	entry_set_int_value(entry, ts->error);
922	entry = new_info_entry_in_frame(box, "PID");
923	entry_set_int_value(entry, ts->pid);
924
925	if (ts->io_bytes[DDIR_READ])
926		gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
927	if (ts->io_bytes[DDIR_WRITE])
928		gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
929
930	gfio_show_latency_buckets(vbox, ts);
931	gfio_show_cpu_usage(vbox, ts);
932	gfio_show_io_depths(vbox, ts);
933
934	gtk_widget_show_all(gc->ge->results_window);
935	gdk_threads_leave();
936}
937
938static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
939{
940	struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
941	struct gui *ui = &main_ui;
942	GtkTreeIter iter;
943	struct tm *tm;
944	time_t sec;
945	char tmp[64], timebuf[80];
946
947	sec = p->log_sec;
948	tm = localtime(&sec);
949	strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
950	sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
951
952	gdk_threads_enter();
953
954	gtk_list_store_append(ui->log_model, &iter);
955	gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
956	gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
957	gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
958	gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
959
960	if (p->level == FIO_LOG_ERR)
961		view_log(NULL, (gpointer) ui);
962
963	gdk_threads_leave();
964}
965
966static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
967{
968	struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
969	struct gfio_client *gc = client->client_data;
970	GtkWidget *box, *frame, *entry, *vbox;
971	double util;
972	char tmp[16];
973
974	gdk_threads_enter();
975
976	if (!gc->results_widget)
977		goto out;
978
979	if (!gc->disk_util_frame) {
980		gc->disk_util_frame = gtk_frame_new("Disk utilization");
981		gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
982	}
983
984	vbox = gtk_vbox_new(FALSE, 3);
985	gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
986
987	frame = gtk_frame_new((char *) p->dus.name);
988	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
989
990	box = gtk_vbox_new(FALSE, 3);
991	gtk_container_add(GTK_CONTAINER(frame), box);
992
993	frame = gtk_frame_new("Read");
994	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
995	vbox = gtk_hbox_new(TRUE, 3);
996	gtk_container_add(GTK_CONTAINER(frame), vbox);
997	entry = new_info_entry_in_frame(vbox, "IOs");
998	entry_set_int_value(entry, p->dus.ios[0]);
999	entry = new_info_entry_in_frame(vbox, "Merges");
1000	entry_set_int_value(entry, p->dus.merges[0]);
1001	entry = new_info_entry_in_frame(vbox, "Sectors");
1002	entry_set_int_value(entry, p->dus.sectors[0]);
1003	entry = new_info_entry_in_frame(vbox, "Ticks");
1004	entry_set_int_value(entry, p->dus.ticks[0]);
1005
1006	frame = gtk_frame_new("Write");
1007	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1008	vbox = gtk_hbox_new(TRUE, 3);
1009	gtk_container_add(GTK_CONTAINER(frame), vbox);
1010	entry = new_info_entry_in_frame(vbox, "IOs");
1011	entry_set_int_value(entry, p->dus.ios[1]);
1012	entry = new_info_entry_in_frame(vbox, "Merges");
1013	entry_set_int_value(entry, p->dus.merges[1]);
1014	entry = new_info_entry_in_frame(vbox, "Sectors");
1015	entry_set_int_value(entry, p->dus.sectors[1]);
1016	entry = new_info_entry_in_frame(vbox, "Ticks");
1017	entry_set_int_value(entry, p->dus.ticks[1]);
1018
1019	frame = gtk_frame_new("Shared");
1020	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1021	vbox = gtk_hbox_new(TRUE, 3);
1022	gtk_container_add(GTK_CONTAINER(frame), vbox);
1023	entry = new_info_entry_in_frame(vbox, "IO ticks");
1024	entry_set_int_value(entry, p->dus.io_ticks);
1025	entry = new_info_entry_in_frame(vbox, "Time in queue");
1026	entry_set_int_value(entry, p->dus.time_in_queue);
1027
1028	util = 0.0;
1029	if (p->dus.msec)
1030		util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1031	if (util > 100.0)
1032		util = 100.0;
1033
1034	sprintf(tmp, "%3.2f%%", util);
1035	entry = new_info_entry_in_frame(vbox, "Disk utilization");
1036	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1037
1038	gtk_widget_show_all(gc->results_widget);
1039out:
1040	gdk_threads_leave();
1041}
1042
1043extern int sum_stat_clients;
1044extern struct thread_stat client_ts;
1045extern struct group_run_stats client_gs;
1046
1047static int sum_stat_nr;
1048
1049static void gfio_thread_status_op(struct fio_client *client,
1050				  struct fio_net_cmd *cmd)
1051{
1052	struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1053
1054	gfio_display_ts(client, &p->ts, &p->rs);
1055
1056	if (sum_stat_clients == 1)
1057		return;
1058
1059	sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1060	sum_group_stats(&client_gs, &p->rs);
1061
1062	client_ts.members++;
1063	client_ts.groupid = p->ts.groupid;
1064
1065	if (++sum_stat_nr == sum_stat_clients) {
1066		strcpy(client_ts.name, "All clients");
1067		gfio_display_ts(client, &client_ts, &client_gs);
1068	}
1069}
1070
1071static void gfio_group_stats_op(struct fio_client *client,
1072				struct fio_net_cmd *cmd)
1073{
1074	/* We're ignoring group stats for now */
1075}
1076
1077static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1078				   gpointer data)
1079{
1080	struct gfio_graphs *g = data;
1081
1082	g->drawing_area_xdim = w->allocation.width;
1083	g->drawing_area_ydim = w->allocation.height;
1084	return TRUE;
1085}
1086
1087static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1088{
1089	struct gfio_graphs *g = p;
1090	cairo_t *cr;
1091
1092	graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1093					g->drawing_area_ydim);
1094	graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1095					g->drawing_area_ydim);
1096	cr = gdk_cairo_create(w->window);
1097
1098	cairo_set_source_rgb(cr, 0, 0, 0);
1099
1100	cairo_save(cr);
1101	cairo_translate(cr, 0, 0);
1102	line_graph_draw(g->bandwidth_graph, cr);
1103	cairo_stroke(cr);
1104	cairo_restore(cr);
1105
1106	cairo_save(cr);
1107	cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1108	line_graph_draw(g->iops_graph, cr);
1109	cairo_stroke(cr);
1110	cairo_restore(cr);
1111	cairo_destroy(cr);
1112
1113	return FALSE;
1114}
1115
1116/*
1117 * Client specific ETA
1118 */
1119static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1120{
1121	struct gfio_client *gc = client->client_data;
1122	struct gui_entry *ge = gc->ge;
1123	static int eta_good;
1124	char eta_str[128];
1125	char output[256];
1126	char tmp[32];
1127	double perc = 0.0;
1128	int i2p = 0;
1129
1130	gdk_threads_enter();
1131
1132	eta_str[0] = '\0';
1133	output[0] = '\0';
1134
1135	if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1136		perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1137		eta_to_str(eta_str, je->eta_sec);
1138	}
1139
1140	sprintf(tmp, "%u", je->nr_running);
1141	gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1142	sprintf(tmp, "%u", je->files_open);
1143	gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1144
1145#if 0
1146	if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1147	if (je->m_rate || je->t_rate) {
1148		char *tr, *mr;
1149
1150		mr = num2str(je->m_rate, 4, 0, i2p);
1151		tr = num2str(je->t_rate, 4, 0, i2p);
1152		gtk_entry_set_text(GTK_ENTRY(ge->eta);
1153		p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1154		free(tr);
1155		free(mr);
1156	} else if (je->m_iops || je->t_iops)
1157		p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1158
1159	gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1160	gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1161	gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1162	gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1163#endif
1164
1165	if (je->eta_sec != INT_MAX && je->nr_running) {
1166		char *iops_str[2];
1167		char *rate_str[2];
1168
1169		if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1170			strcpy(output, "-.-% done");
1171		else {
1172			eta_good = 1;
1173			perc *= 100.0;
1174			sprintf(output, "%3.1f%% done", perc);
1175		}
1176
1177		rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1178		rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1179
1180		iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1181		iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1182
1183		gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1184		gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1185		gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1186		gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1187
1188		graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1189		graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1190		graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1191		graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1192
1193		free(rate_str[0]);
1194		free(rate_str[1]);
1195		free(iops_str[0]);
1196		free(iops_str[1]);
1197	}
1198
1199	if (eta_str[0]) {
1200		char *dst = output + strlen(output);
1201
1202		sprintf(dst, " - %s", eta_str);
1203	}
1204
1205	gfio_update_thread_status(ge, output, perc);
1206	gdk_threads_leave();
1207}
1208
1209/*
1210 * Update ETA in main window for all clients
1211 */
1212static void gfio_update_all_eta(struct jobs_eta *je)
1213{
1214	struct gui *ui = &main_ui;
1215	static int eta_good;
1216	char eta_str[128];
1217	char output[256];
1218	double perc = 0.0;
1219	int i2p = 0;
1220
1221	gdk_threads_enter();
1222
1223	eta_str[0] = '\0';
1224	output[0] = '\0';
1225
1226	if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1227		perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1228		eta_to_str(eta_str, je->eta_sec);
1229	}
1230
1231#if 0
1232	if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1233	if (je->m_rate || je->t_rate) {
1234		char *tr, *mr;
1235
1236		mr = num2str(je->m_rate, 4, 0, i2p);
1237		tr = num2str(je->t_rate, 4, 0, i2p);
1238		gtk_entry_set_text(GTK_ENTRY(ui->eta);
1239		p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1240		free(tr);
1241		free(mr);
1242	} else if (je->m_iops || je->t_iops)
1243		p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1244
1245	gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1246	gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1247	gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1248	gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1249#endif
1250
1251	if (je->eta_sec != INT_MAX && je->nr_running) {
1252		char *iops_str[2];
1253		char *rate_str[2];
1254
1255		if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1256			strcpy(output, "-.-% done");
1257		else {
1258			eta_good = 1;
1259			perc *= 100.0;
1260			sprintf(output, "%3.1f%% done", perc);
1261		}
1262
1263		rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1264		rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1265
1266		iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1267		iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1268
1269		gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1270		gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1271		gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1272		gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1273
1274		graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1275		graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1276		graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1277		graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1278
1279		free(rate_str[0]);
1280		free(rate_str[1]);
1281		free(iops_str[0]);
1282		free(iops_str[1]);
1283	}
1284
1285	if (eta_str[0]) {
1286		char *dst = output + strlen(output);
1287
1288		sprintf(dst, " - %s", eta_str);
1289	}
1290
1291	gfio_update_thread_status_all(output, perc);
1292	gdk_threads_leave();
1293}
1294
1295static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1296{
1297	struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1298	struct gfio_client *gc = client->client_data;
1299	struct gui_entry *ge = gc->ge;
1300	const char *os, *arch;
1301	char buf[64];
1302
1303	os = fio_get_os_string(probe->os);
1304	if (!os)
1305		os = "unknown";
1306
1307	arch = fio_get_arch_string(probe->arch);
1308	if (!arch)
1309		os = "unknown";
1310
1311	if (!client->name)
1312		client->name = strdup((char *) probe->hostname);
1313
1314	gdk_threads_enter();
1315
1316	gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1317	gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1318	gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1319	sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1320	gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1321
1322	gfio_set_connected(ge, 1);
1323
1324	gdk_threads_leave();
1325}
1326
1327static void gfio_update_thread_status(struct gui_entry *ge,
1328				      char *status_message, double perc)
1329{
1330	static char message[100];
1331	const char *m = message;
1332
1333	strncpy(message, status_message, sizeof(message) - 1);
1334	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1335	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1336	gtk_widget_queue_draw(main_ui.window);
1337}
1338
1339static void gfio_update_thread_status_all(char *status_message, double perc)
1340{
1341	struct gui *ui = &main_ui;
1342	static char message[100];
1343	const char *m = message;
1344
1345	strncpy(message, status_message, sizeof(message) - 1);
1346	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1347	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1348	gtk_widget_queue_draw(ui->window);
1349}
1350
1351static void gfio_quit_op(struct fio_client *client)
1352{
1353	struct gfio_client *gc = client->client_data;
1354
1355	gdk_threads_enter();
1356	gfio_set_connected(gc->ge, 0);
1357	gdk_threads_leave();
1358}
1359
1360static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1361{
1362	struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1363	struct gfio_client *gc = client->client_data;
1364	struct thread_options *o = &gc->o;
1365	struct gui_entry *ge = gc->ge;
1366	char tmp[8];
1367
1368	convert_thread_options_to_cpu(o, &p->top);
1369
1370	gdk_threads_enter();
1371
1372	gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1373
1374	gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1375	gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1376	gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1377
1378	sprintf(tmp, "%u", o->iodepth);
1379	gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1380
1381	gc->job_added++;
1382
1383	gdk_threads_leave();
1384}
1385
1386static void gfio_client_timed_out(struct fio_client *client)
1387{
1388	struct gfio_client *gc = client->client_data;
1389	GtkWidget *dialog, *label, *content;
1390	char buf[256];
1391
1392	gdk_threads_enter();
1393
1394	gfio_set_connected(gc->ge, 0);
1395	clear_ge_ui_info(gc->ge);
1396
1397	sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1398
1399	dialog = gtk_dialog_new_with_buttons("Timed out!",
1400			GTK_WINDOW(main_ui.window),
1401			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1402			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1403
1404	/* gtk_dialog_get_content_area() is 2.14 and newer */
1405	content = GTK_DIALOG(dialog)->vbox;
1406
1407	label = gtk_label_new((const gchar *) buf);
1408	gtk_container_add(GTK_CONTAINER(content), label);
1409	gtk_widget_show_all(dialog);
1410	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1411
1412	gtk_dialog_run(GTK_DIALOG(dialog));
1413	gtk_widget_destroy(dialog);
1414
1415	gdk_threads_leave();
1416}
1417
1418static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1419{
1420	struct gfio_client *gc = client->client_data;
1421
1422	gdk_threads_enter();
1423
1424	gfio_set_connected(gc->ge, 0);
1425
1426	if (gc->err_entry)
1427		entry_set_int_value(gc->err_entry, client->error);
1428
1429	gdk_threads_leave();
1430}
1431
1432struct client_ops gfio_client_ops = {
1433	.text_op		= gfio_text_op,
1434	.disk_util		= gfio_disk_util_op,
1435	.thread_status		= gfio_thread_status_op,
1436	.group_stats		= gfio_group_stats_op,
1437	.jobs_eta		= gfio_update_client_eta,
1438	.eta			= gfio_update_all_eta,
1439	.probe			= gfio_probe_op,
1440	.quit			= gfio_quit_op,
1441	.add_job		= gfio_add_job_op,
1442	.timed_out		= gfio_client_timed_out,
1443	.stop			= gfio_client_stop,
1444	.stay_connected		= 1,
1445};
1446
1447static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1448                __attribute__((unused)) gpointer data)
1449{
1450        gtk_main_quit();
1451}
1452
1453static void *job_thread(void *arg)
1454{
1455	struct gui *ui = arg;
1456
1457	ui->handler_running = 1;
1458	fio_handle_clients(&gfio_client_ops);
1459	ui->handler_running = 0;
1460	return NULL;
1461}
1462
1463static int send_job_files(struct gui_entry *ge)
1464{
1465	struct gfio_client *gc = ge->client;
1466	int i, ret = 0;
1467
1468	for (i = 0; i < ge->nr_job_files; i++) {
1469		ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1470		if (ret)
1471			break;
1472
1473		free(ge->job_files[i]);
1474		ge->job_files[i] = NULL;
1475	}
1476	while (i < ge->nr_job_files) {
1477		free(ge->job_files[i]);
1478		ge->job_files[i] = NULL;
1479		i++;
1480	}
1481
1482	return ret;
1483}
1484
1485static void *server_thread(void *arg)
1486{
1487	is_backend = 1;
1488	gfio_server_running = 1;
1489	fio_start_server(NULL);
1490	gfio_server_running = 0;
1491	return NULL;
1492}
1493
1494static void gfio_start_server(void)
1495{
1496	struct gui *ui = &main_ui;
1497
1498	if (!gfio_server_running) {
1499		gfio_server_running = 1;
1500		pthread_create(&ui->server_t, NULL, server_thread, NULL);
1501		pthread_detach(ui->server_t);
1502	}
1503}
1504
1505static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1506                gpointer data)
1507{
1508	struct gui_entry *ge = data;
1509	struct gfio_client *gc = ge->client;
1510
1511	gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1512	fio_start_client(gc->client);
1513}
1514
1515static void file_open(GtkWidget *w, gpointer data);
1516
1517static void connect_clicked(GtkWidget *widget, gpointer data)
1518{
1519	struct gui_entry *ge = data;
1520	struct gfio_client *gc = ge->client;
1521
1522	if (!ge->connected) {
1523		if (!ge->nr_job_files)
1524			file_open(widget, data);
1525		if (!ge->nr_job_files)
1526			return;
1527
1528		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1529		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1530		if (!fio_client_connect(gc->client)) {
1531			if (!ge->ui->handler_running)
1532				pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1533			gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1534			gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1535		}
1536	} else {
1537		fio_client_terminate(gc->client);
1538		gfio_set_connected(ge, 0);
1539		clear_ge_ui_info(ge);
1540	}
1541}
1542
1543static void send_clicked(GtkWidget *widget, gpointer data)
1544{
1545	struct gui_entry *ge = data;
1546
1547	if (send_job_files(ge)) {
1548		printf("Yeah, I didn't really like those options too much.\n");
1549		gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1550	}
1551
1552	gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1553	gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1554}
1555
1556static GtkWidget *add_button(GtkWidget *buttonbox,
1557			     struct button_spec *buttonspec, gpointer data)
1558{
1559	GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1560
1561	g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1562	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1563	gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1564	gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1565
1566	return button;
1567}
1568
1569static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1570			int nbuttons)
1571{
1572	int i;
1573
1574	for (i = 0; i < nbuttons; i++)
1575		ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1576}
1577
1578static void on_info_bar_response(GtkWidget *widget, gint response,
1579                                 gpointer data)
1580{
1581	struct gui *ui = &main_ui;
1582
1583	if (response == GTK_RESPONSE_OK) {
1584		gtk_widget_destroy(widget);
1585		ui->error_info_bar = NULL;
1586	}
1587}
1588
1589void report_error(GError *error)
1590{
1591	struct gui *ui = &main_ui;
1592
1593	if (ui->error_info_bar == NULL) {
1594		ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1595		                                               GTK_RESPONSE_OK,
1596		                                               NULL);
1597		g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1598		gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1599		                              GTK_MESSAGE_ERROR);
1600
1601		ui->error_label = gtk_label_new(error->message);
1602		GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1603		gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1604
1605		gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1606		gtk_widget_show_all(ui->vbox);
1607	} else {
1608		char buffer[256];
1609		snprintf(buffer, sizeof(buffer), "Failed to open file.");
1610		gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1611	}
1612}
1613
1614struct connection_widgets
1615{
1616	GtkWidget *hentry;
1617	GtkWidget *combo;
1618	GtkWidget *button;
1619};
1620
1621static void hostname_cb(GtkEntry *entry, gpointer data)
1622{
1623	struct connection_widgets *cw = data;
1624	int uses_net = 0, is_localhost = 0;
1625	const gchar *text;
1626	gchar *ctext;
1627
1628	/*
1629	 * Check whether to display the 'auto start backend' box
1630	 * or not. Show it if we are a localhost and using network,
1631	 * or using a socket.
1632	 */
1633	ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1634	if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1635		uses_net = 1;
1636	g_free(ctext);
1637
1638	if (uses_net) {
1639		text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1640		if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1641		    !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1642		    !strcmp(text, "ip6-loopback"))
1643			is_localhost = 1;
1644	}
1645
1646	if (!uses_net || is_localhost) {
1647		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1648		gtk_widget_set_sensitive(cw->button, 1);
1649	} else {
1650		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1651		gtk_widget_set_sensitive(cw->button, 0);
1652	}
1653}
1654
1655static int get_connection_details(char **host, int *port, int *type,
1656				  int *server_start)
1657{
1658	GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1659	struct connection_widgets cw;
1660	char *typeentry;
1661
1662	dialog = gtk_dialog_new_with_buttons("Connection details",
1663			GTK_WINDOW(main_ui.window),
1664			GTK_DIALOG_DESTROY_WITH_PARENT,
1665			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1666			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1667
1668	frame = gtk_frame_new("Hostname / socket name");
1669	/* gtk_dialog_get_content_area() is 2.14 and newer */
1670	vbox = GTK_DIALOG(dialog)->vbox;
1671	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1672
1673	box = gtk_vbox_new(FALSE, 6);
1674	gtk_container_add(GTK_CONTAINER(frame), box);
1675
1676	hbox = gtk_hbox_new(TRUE, 10);
1677	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1678	cw.hentry = gtk_entry_new();
1679	gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1680	gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1681
1682	frame = gtk_frame_new("Port");
1683	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1684	box = gtk_vbox_new(FALSE, 10);
1685	gtk_container_add(GTK_CONTAINER(frame), box);
1686
1687	hbox = gtk_hbox_new(TRUE, 4);
1688	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1689	pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1690
1691	frame = gtk_frame_new("Type");
1692	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1693	box = gtk_vbox_new(FALSE, 10);
1694	gtk_container_add(GTK_CONTAINER(frame), box);
1695
1696	hbox = gtk_hbox_new(TRUE, 4);
1697	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1698
1699	cw.combo = gtk_combo_box_new_text();
1700	gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1701	gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1702	gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1703	gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1704
1705	gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1706
1707	frame = gtk_frame_new("Options");
1708	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1709	box = gtk_vbox_new(FALSE, 10);
1710	gtk_container_add(GTK_CONTAINER(frame), box);
1711
1712	hbox = gtk_hbox_new(TRUE, 4);
1713	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1714
1715	cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1716	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1717	gtk_widget_set_tooltip_text(cw.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.");
1718	gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1719
1720	/*
1721	 * Connect edit signal, so we can show/not-show the auto start button
1722	 */
1723	g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1724	g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1725
1726	gtk_widget_show_all(dialog);
1727
1728	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1729		gtk_widget_destroy(dialog);
1730		return 1;
1731	}
1732
1733	*host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1734	*port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1735
1736	typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1737	if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1738		*type = Fio_client_ipv4;
1739	else if (!strncmp(typeentry, "IPv6", 4))
1740		*type = Fio_client_ipv6;
1741	else
1742		*type = Fio_client_socket;
1743	g_free(typeentry);
1744
1745	*server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1746
1747	gtk_widget_destroy(dialog);
1748	return 0;
1749}
1750
1751static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1752{
1753	struct gfio_client *gc;
1754
1755	gc = malloc(sizeof(*gc));
1756	memset(gc, 0, sizeof(*gc));
1757	gc->ge = ge;
1758	gc->client = client;
1759
1760	ge->client = gc;
1761
1762	client->client_data = gc;
1763}
1764
1765static GtkWidget *new_client_page(struct gui_entry *ge);
1766
1767static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1768{
1769	struct gui_entry *ge;
1770
1771	ge = malloc(sizeof(*ge));
1772	memset(ge, 0, sizeof(*ge));
1773	INIT_FLIST_HEAD(&ge->list);
1774	flist_add_tail(&ge->list, &ui->list);
1775	ge->ui = ui;
1776	return ge;
1777}
1778
1779/*
1780 * FIXME: need more handling here
1781 */
1782static void ge_destroy(GtkWidget *w, gpointer data)
1783{
1784	struct gui_entry *ge = data;
1785
1786	flist_del(&ge->list);
1787	free(ge);
1788}
1789
1790static struct gui_entry *get_new_ge_with_tab(const char *name)
1791{
1792	struct gui_entry *ge;
1793
1794	ge = alloc_new_gui_entry(&main_ui);
1795
1796	ge->vbox = new_client_page(ge);
1797	g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1798
1799	ge->page_label = gtk_label_new(name);
1800	ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1801
1802	gtk_widget_show_all(main_ui.window);
1803	return ge;
1804}
1805
1806static void file_new(GtkWidget *w, gpointer data)
1807{
1808	get_new_ge_with_tab("Untitled");
1809}
1810
1811/*
1812 * Return the 'ge' corresponding to the tab. If the active tab is the
1813 * main tab, open a new tab.
1814 */
1815static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1816{
1817	struct flist_head *entry;
1818	struct gui_entry *ge;
1819
1820	if (!cur_page)
1821		return get_new_ge_with_tab("Untitled");
1822
1823	flist_for_each(entry, &main_ui.list) {
1824		ge = flist_entry(entry, struct gui_entry, list);
1825		if (ge->page_num == cur_page)
1826			return ge;
1827	}
1828
1829	return NULL;
1830}
1831
1832static void file_open(GtkWidget *w, gpointer data)
1833{
1834	struct gui *ui = data;
1835	GtkWidget *dialog;
1836	GSList *filenames, *fn_glist;
1837	GtkFileFilter *filter;
1838	char *host;
1839	int port, type, server_start;
1840	struct gui_entry *ge;
1841	gint cur_page;
1842
1843	/*
1844	 * Creates new tab if current tab is the main window, or the
1845	 * current tab already has a client.
1846	 */
1847	cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1848	ge = get_ge_from_page(cur_page);
1849	if (ge->client)
1850		ge = get_new_ge_with_tab("Untitled");
1851
1852	gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1853
1854	dialog = gtk_file_chooser_dialog_new("Open File",
1855		GTK_WINDOW(ui->window),
1856		GTK_FILE_CHOOSER_ACTION_OPEN,
1857		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1858		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1859		NULL);
1860	gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1861
1862	filter = gtk_file_filter_new();
1863	gtk_file_filter_add_pattern(filter, "*.fio");
1864	gtk_file_filter_add_pattern(filter, "*.job");
1865	gtk_file_filter_add_pattern(filter, "*.ini");
1866	gtk_file_filter_add_mime_type(filter, "text/fio");
1867	gtk_file_filter_set_name(filter, "Fio job file");
1868	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1869
1870	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1871		gtk_widget_destroy(dialog);
1872		return;
1873	}
1874
1875	fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1876
1877	gtk_widget_destroy(dialog);
1878
1879	if (get_connection_details(&host, &port, &type, &server_start))
1880		goto err;
1881
1882	filenames = fn_glist;
1883	while (filenames != NULL) {
1884		struct fio_client *client;
1885
1886		ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1887		ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1888		ge->nr_job_files++;
1889
1890		client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1891		if (!client) {
1892			GError *error;
1893
1894			error = g_error_new(g_quark_from_string("fio"), 1,
1895					"Failed to add client %s", host);
1896			report_error(error);
1897			g_error_free(error);
1898		}
1899		gfio_client_added(ge, client);
1900
1901		g_free(filenames->data);
1902		filenames = g_slist_next(filenames);
1903	}
1904	free(host);
1905
1906	if (server_start)
1907		gfio_start_server();
1908err:
1909	g_slist_free(fn_glist);
1910}
1911
1912static void file_save(GtkWidget *w, gpointer data)
1913{
1914	struct gui *ui = data;
1915	GtkWidget *dialog;
1916
1917	dialog = gtk_file_chooser_dialog_new("Save File",
1918		GTK_WINDOW(ui->window),
1919		GTK_FILE_CHOOSER_ACTION_SAVE,
1920		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1921		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1922		NULL);
1923
1924	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1925	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1926
1927	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1928		char *filename;
1929
1930		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1931		// save_job_file(filename);
1932		g_free(filename);
1933	}
1934	gtk_widget_destroy(dialog);
1935}
1936
1937static void view_log_destroy(GtkWidget *w, gpointer data)
1938{
1939	struct gui *ui = (struct gui *) data;
1940
1941	gtk_widget_ref(ui->log_tree);
1942	gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1943	gtk_widget_destroy(w);
1944	ui->log_view = NULL;
1945}
1946
1947static void view_log(GtkWidget *w, gpointer data)
1948{
1949	GtkWidget *win, *scroll, *vbox, *box;
1950	struct gui *ui = (struct gui *) data;
1951
1952	if (ui->log_view)
1953		return;
1954
1955	ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1956	gtk_window_set_title(GTK_WINDOW(win), "Log");
1957	gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1958
1959	scroll = gtk_scrolled_window_new(NULL, NULL);
1960
1961	gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1962
1963	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1964
1965	box = gtk_hbox_new(TRUE, 0);
1966	gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1967	g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1968	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1969
1970	vbox = gtk_vbox_new(TRUE, 5);
1971	gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1972
1973	gtk_container_add(GTK_CONTAINER(win), vbox);
1974	gtk_widget_show_all(win);
1975}
1976
1977static void preferences(GtkWidget *w, gpointer data)
1978{
1979	GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1980	int i;
1981
1982	dialog = gtk_dialog_new_with_buttons("Preferences",
1983		GTK_WINDOW(main_ui.window),
1984		GTK_DIALOG_DESTROY_WITH_PARENT,
1985		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1986		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1987		NULL);
1988
1989	frame = gtk_frame_new("Debug logging");
1990	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1991
1992	vbox = gtk_vbox_new(FALSE, 6);
1993	gtk_container_add(GTK_CONTAINER(frame), vbox);
1994
1995	box = gtk_hbox_new(FALSE, 6);
1996	gtk_container_add(GTK_CONTAINER(vbox), box);
1997
1998	buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1999
2000	for (i = 0; i < FD_DEBUG_MAX; i++) {
2001		if (i == 7) {
2002			box = gtk_hbox_new(FALSE, 6);
2003			gtk_container_add(GTK_CONTAINER(vbox), box);
2004		}
2005
2006
2007		buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2008		gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2009		gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2010	}
2011
2012	frame = gtk_frame_new("Graph font");
2013	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2014	vbox = gtk_vbox_new(FALSE, 6);
2015	gtk_container_add(GTK_CONTAINER(frame), vbox);
2016
2017	font = gtk_font_button_new();
2018	gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2019
2020	gtk_widget_show_all(dialog);
2021
2022	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2023		gtk_widget_destroy(dialog);
2024		return;
2025	}
2026
2027	for (i = 0; i < FD_DEBUG_MAX; i++) {
2028		int set;
2029
2030		set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2031		if (set)
2032			fio_debug |= (1UL << i);
2033	}
2034
2035	gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2036	gtk_widget_destroy(dialog);
2037}
2038
2039static void about_dialog(GtkWidget *w, gpointer data)
2040{
2041	const char *authors[] = {
2042		"Jens Axboe <axboe@kernel.dk>",
2043		"Stephen Carmeron <stephenmcameron@gmail.com>",
2044		NULL
2045	};
2046	const char *license[] = {
2047		"Fio is free software; you can redistribute it and/or modify "
2048		"it under the terms of the GNU General Public License as published by "
2049		"the Free Software Foundation; either version 2 of the License, or "
2050		"(at your option) any later version.\n",
2051		"Fio is distributed in the hope that it will be useful, "
2052		"but WITHOUT ANY WARRANTY; without even the implied warranty of "
2053		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
2054		"GNU General Public License for more details.\n",
2055		"You should have received a copy of the GNU General Public License "
2056		"along with Fio; if not, write to the Free Software Foundation, Inc., "
2057		"51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA\n"
2058	};
2059	char *license_trans;
2060
2061	license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2062				     license[2], "\n", NULL);
2063
2064	gtk_show_about_dialog(NULL,
2065		"program-name", "gfio",
2066		"comments", "Gtk2 UI for fio",
2067		"license", license_trans,
2068		"website", "http://git.kernel.dk/?p=fio.git;a=summary",
2069		"authors", authors,
2070		"version", fio_version_string,
2071		"copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2072		"logo-icon-name", "fio",
2073		/* Must be last: */
2074		"wrap-license", TRUE,
2075		NULL);
2076
2077	g_free(license_trans);
2078}
2079
2080static GtkActionEntry menu_items[] = {
2081	{ "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2082	{ "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2083	{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2084	{ "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2085	{ "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
2086	{ "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
2087	{ "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2088	{ "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2089	{ "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2090	{ "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
2091};
2092static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2093
2094static const gchar *ui_string = " \
2095	<ui> \
2096		<menubar name=\"MainMenu\"> \
2097			<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2098				<menuitem name=\"New\" action=\"NewFile\" /> \
2099				<separator name=\"Separator1\"/> \
2100				<menuitem name=\"Open\" action=\"OpenFile\" /> \
2101				<menuitem name=\"Save\" action=\"SaveFile\" /> \
2102				<separator name=\"Separator2\"/> \
2103				<menuitem name=\"Preferences\" action=\"Preferences\" /> \
2104				<separator name=\"Separator3\"/> \
2105				<menuitem name=\"Quit\" action=\"Quit\" /> \
2106			</menu> \
2107			<menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2108				<menuitem name=\"Log\" action=\"ViewLog\" /> \
2109			</menu>\
2110			<menu name=\"Help\" action=\"HelpMenuAction\"> \
2111				<menuitem name=\"About\" action=\"About\" /> \
2112			</menu> \
2113		</menubar> \
2114	</ui> \
2115";
2116
2117static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2118				   struct gui *ui)
2119{
2120	GtkActionGroup *action_group = gtk_action_group_new("Menu");
2121	GError *error = 0;
2122
2123	action_group = gtk_action_group_new("Menu");
2124	gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2125
2126	gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2127	gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2128
2129	gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2130	return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2131}
2132
2133void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2134                   GtkWidget *vbox, GtkUIManager *ui_manager)
2135{
2136        gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2137}
2138
2139static GtkWidget *new_client_page(struct gui_entry *ge)
2140{
2141	GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2142	GdkColor white;
2143
2144	main_vbox = gtk_vbox_new(FALSE, 3);
2145
2146	ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2147	ge->topvbox = gtk_vbox_new(FALSE, 3);
2148	gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2149	gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2150
2151	probe = gtk_frame_new("Job");
2152	gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2153	probe_frame = gtk_vbox_new(FALSE, 3);
2154	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2155
2156	probe_box = gtk_hbox_new(FALSE, 3);
2157	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2158	ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2159	ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2160	ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2161	ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2162
2163	probe_box = gtk_hbox_new(FALSE, 3);
2164	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2165
2166	ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2167	ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2168	ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2169	ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2170	ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2171	ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2172
2173	probe_box = gtk_hbox_new(FALSE, 3);
2174	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2175	ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2176	ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2177	ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2178	ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2179
2180	/*
2181	 * Only add this if we have a commit rate
2182	 */
2183#if 0
2184	probe_box = gtk_hbox_new(FALSE, 3);
2185	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2186
2187	ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2188	ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2189
2190	ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2191	ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2192#endif
2193
2194	/*
2195	 * Set up a drawing area and IOPS and bandwidth graphs
2196	 */
2197	gdk_color_parse("white", &white);
2198	ge->graphs.drawing_area = gtk_drawing_area_new();
2199	ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2200	ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2201	gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2202		ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2203	gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2204	g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2205				G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2206	g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2207				G_CALLBACK(on_config_drawing_area), &ge->graphs);
2208	ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2209	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2210					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2211	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2212					ge->graphs.drawing_area);
2213	gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2214			TRUE, TRUE, 0);
2215
2216	setup_graphs(&ge->graphs);
2217
2218	/*
2219	 * Set up alignments for widgets at the bottom of ui,
2220	 * align bottom left, expand horizontally but not vertically
2221	 */
2222	ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2223	ge->buttonbox = gtk_hbox_new(FALSE, 0);
2224	gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2225	gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2226					FALSE, FALSE, 0);
2227
2228	add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2229
2230	/*
2231	 * Set up thread status progress bar
2232	 */
2233	ge->thread_status_pb = gtk_progress_bar_new();
2234	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2235	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2236	gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2237
2238
2239	return main_vbox;
2240}
2241
2242static GtkWidget *new_main_page(struct gui *ui)
2243{
2244	GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2245	GdkColor white;
2246
2247	main_vbox = gtk_vbox_new(FALSE, 3);
2248
2249	/*
2250	 * Set up alignments for widgets at the top of ui,
2251	 * align top left, expand horizontally but not vertically
2252	 */
2253	ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2254	ui->topvbox = gtk_vbox_new(FALSE, 0);
2255	gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2256	gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2257
2258	probe = gtk_frame_new("Run statistics");
2259	gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2260	probe_frame = gtk_vbox_new(FALSE, 3);
2261	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2262
2263	probe_box = gtk_hbox_new(FALSE, 3);
2264	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2265	ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2266	ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2267	ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2268	ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2269
2270	/*
2271	 * Only add this if we have a commit rate
2272	 */
2273#if 0
2274	probe_box = gtk_hbox_new(FALSE, 3);
2275	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2276
2277	ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2278	ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2279
2280	ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2281	ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2282#endif
2283
2284	/*
2285	 * Set up a drawing area and IOPS and bandwidth graphs
2286	 */
2287	gdk_color_parse("white", &white);
2288	ui->graphs.drawing_area = gtk_drawing_area_new();
2289	ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2290	ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2291	gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2292		ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2293	gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2294	g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2295			G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2296	g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2297			G_CALLBACK(on_config_drawing_area), &ui->graphs);
2298	ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2299	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2300					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2301	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2302					ui->graphs.drawing_area);
2303	gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2304			TRUE, TRUE, 0);
2305
2306	setup_graphs(&ui->graphs);
2307
2308	/*
2309	 * Set up alignments for widgets at the bottom of ui,
2310	 * align bottom left, expand horizontally but not vertically
2311	 */
2312	ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2313	ui->buttonbox = gtk_hbox_new(FALSE, 0);
2314	gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2315	gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2316					FALSE, FALSE, 0);
2317
2318	/*
2319	 * Set up thread status progress bar
2320	 */
2321	ui->thread_status_pb = gtk_progress_bar_new();
2322	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2323	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2324	gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2325
2326	return main_vbox;
2327}
2328
2329static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2330				     guint page, gpointer data)
2331
2332{
2333	return TRUE;
2334}
2335
2336static void init_ui(int *argc, char **argv[], struct gui *ui)
2337{
2338	GtkSettings *settings;
2339	GtkUIManager *uimanager;
2340	GtkWidget *menu, *vbox;
2341
2342	/* Magical g*thread incantation, you just need this thread stuff.
2343	 * Without it, the update that happens in gfio_update_thread_status
2344	 * doesn't really happen in a timely fashion, you need expose events
2345	 */
2346	if (!g_thread_supported())
2347		g_thread_init(NULL);
2348	gdk_threads_init();
2349
2350	gtk_init(argc, argv);
2351	settings = gtk_settings_get_default();
2352	gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2353	g_type_init();
2354
2355	ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2356        gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2357	gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2358
2359	g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2360	g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2361
2362	ui->vbox = gtk_vbox_new(FALSE, 0);
2363	gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2364
2365	uimanager = gtk_ui_manager_new();
2366	menu = get_menubar_menu(ui->window, uimanager, ui);
2367	gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2368
2369	ui->notebook = gtk_notebook_new();
2370	g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2371	gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2372
2373	vbox = new_main_page(ui);
2374
2375	gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2376
2377	gfio_ui_setup_log(ui);
2378
2379	gtk_widget_show_all(ui->window);
2380}
2381
2382int main(int argc, char *argv[], char *envp[])
2383{
2384	if (initialize_fio(envp))
2385		return 1;
2386	if (fio_init_options())
2387		return 1;
2388
2389	memset(&main_ui, 0, sizeof(main_ui));
2390	INIT_FLIST_HEAD(&main_ui.list);
2391
2392	init_ui(&argc, &argv, &main_ui);
2393
2394	gdk_threads_enter();
2395	gtk_main();
2396	gdk_threads_leave();
2397	return 0;
2398}
2399