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