gfio.c revision 2d262990f5770b402fb83ee194a468e31dc8600d
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
27#include <glib.h>
28#include <cairo.h>
29#include <gtk/gtk.h>
30
31#include "fio.h"
32#include "graph.h"
33
34static int gfio_server_running;
35
36static void gfio_update_thread_status(char *status_message, double perc);
37
38#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
39
40typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
41
42static void connect_clicked(GtkWidget *widget, gpointer data);
43static void start_job_clicked(GtkWidget *widget, gpointer data);
44
45static struct button_spec {
46	const char *buttontext;
47	clickfunction f;
48	const char *tooltiptext;
49	const int start_insensitive;
50} buttonspeclist[] = {
51#define CONNECT_BUTTON 0
52#define START_JOB_BUTTON 1
53	{ "Connect", connect_clicked, "Connect to host", 0 },
54	{ "Start Job",
55		start_job_clicked,
56		"Send current fio job to fio server to be executed", 1 },
57};
58
59struct probe_widget {
60	GtkWidget *hostname;
61	GtkWidget *os;
62	GtkWidget *arch;
63	GtkWidget *fio_ver;
64};
65
66struct eta_widget {
67	GtkWidget *name;
68	GtkWidget *iotype;
69	GtkWidget *ioengine;
70	GtkWidget *iodepth;
71	GtkWidget *jobs;
72	GtkWidget *files;
73	GtkWidget *read_bw;
74	GtkWidget *read_iops;
75	GtkWidget *cr_bw;
76	GtkWidget *cr_iops;
77	GtkWidget *write_bw;
78	GtkWidget *write_iops;
79	GtkWidget *cw_bw;
80	GtkWidget *cw_iops;
81};
82
83struct gui {
84	GtkWidget *window;
85	GtkWidget *vbox;
86	GtkWidget *topvbox;
87	GtkWidget *topalign;
88	GtkWidget *bottomalign;
89	GtkWidget *thread_status_pb;
90	GtkWidget *buttonbox;
91	GtkWidget *button[ARRAYSIZE(buttonspeclist)];
92	GtkWidget *scrolled_window;
93#define DRAWING_AREA_XDIM 1000
94#define DRAWING_AREA_YDIM 400
95	GtkWidget *drawing_area;
96	GtkWidget *error_info_bar;
97	GtkWidget *error_label;
98	GtkWidget *results_notebook;
99	GtkWidget *results_window;
100	GtkListStore *log_model;
101	GtkWidget *log_tree;
102	GtkWidget *log_view;
103	GtkTextBuffer *text;
104	struct probe_widget probe;
105	struct eta_widget eta;
106	int connected;
107	pthread_t t;
108	pthread_t server_t;
109
110	struct graph *iops_graph;
111	struct graph *bandwidth_graph;
112	struct fio_client *client;
113	int nr_job_files;
114	char **job_files;
115} ui;
116
117struct gfio_client {
118	struct gui *ui;
119	GtkWidget *results_widget;
120	GtkWidget *disk_util_frame;
121};
122
123static void setup_iops_graph(struct gui *ui)
124{
125	if (ui->iops_graph)
126		graph_free(ui->iops_graph);
127	ui->iops_graph = graph_new((int) DRAWING_AREA_XDIM / 2.0,
128					(int) DRAWING_AREA_YDIM);
129	graph_title(ui->iops_graph, "IOPS");
130	graph_x_title(ui->iops_graph, "Time");
131	graph_y_title(ui->iops_graph, "IOPS");
132	graph_add_label(ui->iops_graph, "Read IOPS");
133	graph_add_label(ui->iops_graph, "Write IOPS");
134	graph_set_color(ui->iops_graph, "Read IOPS", 0.7, 0.0, 0.0);
135	graph_set_color(ui->iops_graph, "Write IOPS", 0.0, 0.0, 0.7);
136}
137
138static void setup_bandwidth_graph(struct gui *ui)
139{
140	if (ui->bandwidth_graph)
141		graph_free(ui->bandwidth_graph);
142	ui->bandwidth_graph = graph_new((int) DRAWING_AREA_XDIM / 2.0,
143					(int) DRAWING_AREA_YDIM);
144	graph_title(ui->bandwidth_graph, "Bandwidth");
145	graph_x_title(ui->bandwidth_graph, "Time");
146	graph_y_title(ui->bandwidth_graph, "Bandwidth");
147	graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
148	graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
149	graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.7, 0.0, 0.0);
150	graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 0.0, 0.0, 0.7);
151}
152
153static void clear_ui_info(struct gui *ui)
154{
155	gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
156	gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
157	gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
158	gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
159	gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
160	gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
161	gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
162	gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
163	gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
164	gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
165	gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
166	gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
167	gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
168	gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
169}
170
171static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
172{
173	GtkWidget *entry, *frame;
174
175	frame = gtk_frame_new(label);
176	entry = gtk_entry_new();
177	gtk_entry_set_editable(GTK_ENTRY(entry), 0);
178	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
179	gtk_container_add(GTK_CONTAINER(frame), entry);
180
181	return entry;
182}
183
184static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
185{
186	GtkWidget *label_widget;
187	GtkWidget *frame;
188
189	frame = gtk_frame_new(label);
190	label_widget = gtk_label_new(NULL);
191	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
192	gtk_container_add(GTK_CONTAINER(frame), label_widget);
193
194	return label_widget;
195}
196
197static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
198{
199	GtkWidget *button, *box;
200
201	box = gtk_hbox_new(FALSE, 3);
202	gtk_container_add(GTK_CONTAINER(hbox), box);
203
204	button = gtk_spin_button_new_with_range(min, max, 1.0);
205	gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
206
207	gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
208	gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
209
210	return button;
211}
212
213static void gfio_set_connected(struct gui *ui, int connected)
214{
215	if (connected) {
216		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
217		ui->connected = 1;
218		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
219		gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
220	} else {
221		ui->connected = 0;
222		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
223		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
224		gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
225	}
226}
227
228static void label_set_int_value(GtkWidget *entry, unsigned int val)
229{
230	char tmp[80];
231
232	sprintf(tmp, "%u", val);
233	gtk_label_set_text(GTK_LABEL(entry), tmp);
234}
235
236static void entry_set_int_value(GtkWidget *entry, unsigned int val)
237{
238	char tmp[80];
239
240	sprintf(tmp, "%u", val);
241	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
242}
243
244#define ALIGN_LEFT 1
245#define ALIGN_RIGHT 2
246#define INVISIBLE 4
247#define UNSORTABLE 8
248
249GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
250{
251	GtkCellRenderer *renderer;
252	GtkTreeViewColumn *col;
253	double xalign = 0.0; /* left as default */
254	PangoAlignment align;
255	gboolean visible;
256
257	align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
258		(flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
259		PANGO_ALIGN_CENTER;
260	visible = !(flags & INVISIBLE);
261
262	renderer = gtk_cell_renderer_text_new();
263	col = gtk_tree_view_column_new();
264
265	gtk_tree_view_column_set_title(col, title);
266	if (!(flags & UNSORTABLE))
267		gtk_tree_view_column_set_sort_column_id(col, index);
268	gtk_tree_view_column_set_resizable(col, TRUE);
269	gtk_tree_view_column_pack_start(col, renderer, TRUE);
270	gtk_tree_view_column_add_attribute(col, renderer, "text", index);
271	gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
272	switch (align) {
273	case PANGO_ALIGN_LEFT:
274		xalign = 0.0;
275		break;
276	case PANGO_ALIGN_CENTER:
277		xalign = 0.5;
278		break;
279	case PANGO_ALIGN_RIGHT:
280		xalign = 1.0;
281		break;
282	}
283	gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
284	gtk_tree_view_column_set_visible(col, visible);
285	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
286	return col;
287}
288
289static void gfio_ui_setup_log(struct gui *ui)
290{
291	GtkTreeSelection *selection;
292	GtkListStore *model;
293	GtkWidget *tree_view;
294
295	model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
296
297	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
298	gtk_widget_set_can_focus(tree_view, FALSE);
299
300	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
301	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
302	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
303		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
304
305	tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
306	tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
307	tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
308	tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
309
310	ui->log_model = model;
311	ui->log_tree = tree_view;
312}
313
314static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
315					       fio_fp64_t *plist,
316					       unsigned int len,
317					       const char *base,
318					       unsigned int scale)
319{
320	GType types[FIO_IO_U_LIST_MAX_LEN];
321	GtkWidget *tree_view;
322	GtkTreeSelection *selection;
323	GtkListStore *model;
324	GtkTreeIter iter;
325	int i;
326
327	for (i = 0; i < len; i++)
328		types[i] = G_TYPE_INT;
329
330	model = gtk_list_store_newv(len, types);
331
332	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
333	gtk_widget_set_can_focus(tree_view, FALSE);
334
335	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
336		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
337
338	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
339	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
340
341	for (i = 0; i < len; i++) {
342		char fbuf[8];
343
344		sprintf(fbuf, "%2.2f%%", plist[i].u.f);
345		tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
346	}
347
348	gtk_list_store_append(model, &iter);
349
350	for (i = 0; i < len; i++) {
351		if (scale)
352			ovals[i] = (ovals[i] + 999) / 1000;
353		gtk_list_store_set(model, &iter, i, ovals[i], -1);
354	}
355
356	return tree_view;
357}
358
359static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
360				       int ddir)
361{
362	unsigned int *io_u_plat = ts->io_u_plat[ddir];
363	unsigned long nr = ts->clat_stat[ddir].samples;
364	fio_fp64_t *plist = ts->percentile_list;
365	unsigned int *ovals, len, minv, maxv, scale_down;
366	const char *base;
367	GtkWidget *tree_view, *frame, *hbox;
368	char tmp[64];
369
370	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
371	if (!len)
372		goto out;
373
374	/*
375	 * We default to usecs, but if the value range is such that we
376	 * should scale down to msecs, do that.
377	 */
378	if (minv > 2000 && maxv > 99999) {
379		scale_down = 1;
380		base = "msec";
381	} else {
382		scale_down = 0;
383		base = "usec";
384	}
385
386	tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
387
388	sprintf(tmp, "Completion percentiles (%s)", base);
389	frame = gtk_frame_new(tmp);
390	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
391
392	hbox = gtk_hbox_new(FALSE, 3);
393	gtk_container_add(GTK_CONTAINER(frame), hbox);
394
395	gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
396out:
397	if (ovals)
398		free(ovals);
399}
400
401static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
402			  unsigned long max, double mean, double dev)
403{
404	const char *base = "(usec)";
405	GtkWidget *hbox, *label, *frame;
406	char *minp, *maxp;
407	char tmp[64];
408
409	if (!usec_to_msec(&min, &max, &mean, &dev))
410		base = "(msec)";
411
412	minp = num2str(min, 6, 1, 0);
413	maxp = num2str(max, 6, 1, 0);
414
415	sprintf(tmp, "%s %s", name, base);
416	frame = gtk_frame_new(tmp);
417	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
418
419	hbox = gtk_hbox_new(FALSE, 3);
420	gtk_container_add(GTK_CONTAINER(frame), hbox);
421
422	label = new_info_label_in_frame(hbox, "Minimum");
423	gtk_label_set_text(GTK_LABEL(label), minp);
424	label = new_info_label_in_frame(hbox, "Maximum");
425	gtk_label_set_text(GTK_LABEL(label), maxp);
426	label = new_info_label_in_frame(hbox, "Average");
427	sprintf(tmp, "%5.02f", mean);
428	gtk_label_set_text(GTK_LABEL(label), tmp);
429	label = new_info_label_in_frame(hbox, "Standard deviation");
430	sprintf(tmp, "%5.02f", dev);
431	gtk_label_set_text(GTK_LABEL(label), tmp);
432
433	free(minp);
434	free(maxp);
435
436}
437
438#define GFIO_CLAT	1
439#define GFIO_SLAT	2
440#define GFIO_LAT	4
441
442static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
443				  struct thread_stat *ts, int ddir)
444{
445	const char *ddir_label[2] = { "Read", "Write" };
446	GtkWidget *frame, *label, *box, *vbox, *main_vbox;
447	unsigned long min[3], max[3], runt;
448	unsigned long long bw, iops;
449	unsigned int flags = 0;
450	double mean[3], dev[3];
451	char *io_p, *bw_p, *iops_p;
452	int i2p;
453
454	if (!ts->runtime[ddir])
455		return;
456
457	i2p = is_power_of_2(rs->kb_base);
458	runt = ts->runtime[ddir];
459
460	bw = (1000 * ts->io_bytes[ddir]) / runt;
461	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
462	bw_p = num2str(bw, 6, 1, i2p);
463
464	iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
465	iops_p = num2str(iops, 6, 1, 0);
466
467	box = gtk_hbox_new(FALSE, 3);
468	gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
469
470	frame = gtk_frame_new(ddir_label[ddir]);
471	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
472
473	main_vbox = gtk_vbox_new(FALSE, 3);
474	gtk_container_add(GTK_CONTAINER(frame), main_vbox);
475
476	box = gtk_hbox_new(FALSE, 3);
477	gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
478
479	label = new_info_label_in_frame(box, "IO");
480	gtk_label_set_text(GTK_LABEL(label), io_p);
481	label = new_info_label_in_frame(box, "Bandwidth");
482	gtk_label_set_text(GTK_LABEL(label), bw_p);
483	label = new_info_label_in_frame(box, "IOPS");
484	gtk_label_set_text(GTK_LABEL(label), iops_p);
485	label = new_info_label_in_frame(box, "Runtime (msec)");
486	label_set_int_value(label, ts->runtime[ddir]);
487
488	if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
489		double p_of_agg = 100.0;
490		const char *bw_str = "KB";
491		char tmp[32];
492
493		if (rs->agg[ddir]) {
494			p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
495			if (p_of_agg > 100.0)
496				p_of_agg = 100.0;
497		}
498
499		if (mean[0] > 999999.9) {
500			min[0] /= 1000.0;
501			max[0] /= 1000.0;
502			mean[0] /= 1000.0;
503			dev[0] /= 1000.0;
504			bw_str = "MB";
505		}
506
507		sprintf(tmp, "Bandwidth (%s)", bw_str);
508		frame = gtk_frame_new(tmp);
509		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
510
511		box = gtk_hbox_new(FALSE, 3);
512		gtk_container_add(GTK_CONTAINER(frame), box);
513
514		label = new_info_label_in_frame(box, "Minimum");
515		label_set_int_value(label, min[0]);
516		label = new_info_label_in_frame(box, "Maximum");
517		label_set_int_value(label, max[0]);
518		label = new_info_label_in_frame(box, "Percentage of jobs");
519		sprintf(tmp, "%3.2f%%", p_of_agg);
520		gtk_label_set_text(GTK_LABEL(label), tmp);
521		label = new_info_label_in_frame(box, "Average");
522		sprintf(tmp, "%5.02f", mean[0]);
523		gtk_label_set_text(GTK_LABEL(label), tmp);
524		label = new_info_label_in_frame(box, "Standard deviation");
525		sprintf(tmp, "%5.02f", dev[0]);
526		gtk_label_set_text(GTK_LABEL(label), tmp);
527	}
528
529	if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
530		flags |= GFIO_SLAT;
531	if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
532		flags |= GFIO_CLAT;
533	if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
534		flags |= GFIO_LAT;
535
536	if (flags) {
537		frame = gtk_frame_new("Latency");
538		gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
539
540		vbox = gtk_vbox_new(FALSE, 3);
541		gtk_container_add(GTK_CONTAINER(frame), vbox);
542
543		if (flags & GFIO_SLAT)
544			gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
545		if (flags & GFIO_CLAT)
546			gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
547		if (flags & GFIO_LAT)
548			gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
549	}
550
551	if (ts->clat_percentiles)
552		gfio_show_clat_percentiles(main_vbox, ts, ddir);
553
554
555	free(io_p);
556	free(bw_p);
557	free(iops_p);
558}
559
560static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
561					  const char **labels)
562{
563	GtkWidget *tree_view;
564	GtkTreeSelection *selection;
565	GtkListStore *model;
566	GtkTreeIter iter;
567	GType *types;
568	int i, skipped;
569
570	/*
571	 * Check if all are empty, in which case don't bother
572	 */
573	for (i = 0, skipped = 0; i < num; i++)
574		if (lat[i] <= 0.0)
575			skipped++;
576
577	if (skipped == num)
578		return NULL;
579
580	types = malloc(num * sizeof(GType));
581
582	for (i = 0; i < num; i++)
583		types[i] = G_TYPE_STRING;
584
585	model = gtk_list_store_newv(num, types);
586	free(types);
587	types = NULL;
588
589	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
590	gtk_widget_set_can_focus(tree_view, FALSE);
591
592	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
593		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
594
595	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
596	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
597
598	for (i = 0; i < num; i++)
599		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
600
601	gtk_list_store_append(model, &iter);
602
603	for (i = 0; i < num; i++) {
604		char fbuf[32];
605
606		if (lat[i] <= 0.0)
607			sprintf(fbuf, "0.00");
608		else
609			sprintf(fbuf, "%3.2f%%", lat[i]);
610
611		gtk_list_store_set(model, &iter, i, fbuf, -1);
612	}
613
614	return tree_view;
615}
616
617static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
618{
619	GtkWidget *box, *frame, *tree_view;
620	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
621	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
622	const char *uranges[] = { "2", "4", "10", "20", "50", "100",
623				  "250", "500", "750", "1000", };
624	const char *mranges[] = { "2", "4", "10", "20", "50", "100",
625				  "250", "500", "750", "1000", "2000",
626				  ">= 2000", };
627
628	stat_calc_lat_u(ts, io_u_lat_u);
629	stat_calc_lat_m(ts, io_u_lat_m);
630
631	tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
632	if (tree_view) {
633		frame = gtk_frame_new("Latency buckets (usec)");
634		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
635
636		box = gtk_hbox_new(FALSE, 3);
637		gtk_container_add(GTK_CONTAINER(frame), box);
638		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
639	}
640
641	tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
642	if (tree_view) {
643		frame = gtk_frame_new("Latency buckets (msec)");
644		gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
645
646		box = gtk_hbox_new(FALSE, 3);
647		gtk_container_add(GTK_CONTAINER(frame), box);
648		gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
649	}
650}
651
652static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
653{
654	GtkWidget *box, *frame, *entry;
655	double usr_cpu, sys_cpu;
656	unsigned long runtime;
657	char tmp[32];
658
659	runtime = ts->total_run_time;
660	if (runtime) {
661		double runt = (double) runtime;
662
663		usr_cpu = (double) ts->usr_time * 100 / runt;
664		sys_cpu = (double) ts->sys_time * 100 / runt;
665	} else {
666		usr_cpu = 0;
667		sys_cpu = 0;
668	}
669
670	frame = gtk_frame_new("OS resources");
671	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
672
673	box = gtk_hbox_new(FALSE, 3);
674	gtk_container_add(GTK_CONTAINER(frame), box);
675
676	entry = new_info_entry_in_frame(box, "User CPU");
677	sprintf(tmp, "%3.2f%%", usr_cpu);
678	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
679	entry = new_info_entry_in_frame(box, "System CPU");
680	sprintf(tmp, "%3.2f%%", sys_cpu);
681	gtk_entry_set_text(GTK_ENTRY(entry), tmp);
682	entry = new_info_entry_in_frame(box, "Context switches");
683	entry_set_int_value(entry, ts->ctx);
684	entry = new_info_entry_in_frame(box, "Major faults");
685	entry_set_int_value(entry, ts->majf);
686	entry = new_info_entry_in_frame(box, "Minor faults");
687	entry_set_int_value(entry, ts->minf);
688}
689static void gfio_add_sc_depths_tree(GtkListStore *model,
690				    struct thread_stat *ts, unsigned int len,
691				    int submit)
692{
693	double io_u_dist[FIO_IO_U_MAP_NR];
694	GtkTreeIter iter;
695	/* Bits 0, and 3-8 */
696	const int add_mask = 0x1f9;
697	int i, j;
698
699	if (submit)
700		stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
701	else
702		stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
703
704	gtk_list_store_append(model, &iter);
705
706	gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
707
708	for (i = 1, j = 0; i < len; i++) {
709		char fbuf[32];
710
711		if (!(add_mask & (1UL << (i - 1))))
712			sprintf(fbuf, "0.0%%");
713		else {
714			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
715			j++;
716		}
717
718		gtk_list_store_set(model, &iter, i, fbuf, -1);
719	}
720
721}
722
723static void gfio_add_total_depths_tree(GtkListStore *model,
724				       struct thread_stat *ts, unsigned int len)
725{
726	double io_u_dist[FIO_IO_U_MAP_NR];
727	GtkTreeIter iter;
728	/* Bits 1-6, and 8 */
729	const int add_mask = 0x17e;
730	int i, j;
731
732	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
733
734	gtk_list_store_append(model, &iter);
735
736	gtk_list_store_set(model, &iter, 0, "Total", -1);
737
738	for (i = 1, j = 0; i < len; i++) {
739		char fbuf[32];
740
741		if (!(add_mask & (1UL << (i - 1))))
742			sprintf(fbuf, "0.0%%");
743		else {
744			sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
745			j++;
746		}
747
748		gtk_list_store_set(model, &iter, i, fbuf, -1);
749	}
750
751}
752
753static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
754{
755	GtkWidget *frame, *box, *tree_view;
756	GtkTreeSelection *selection;
757	GtkListStore *model;
758	GType types[FIO_IO_U_MAP_NR + 1];
759	int i;
760#define NR_LABELS	10
761	const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
762
763	frame = gtk_frame_new("IO depths");
764	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
765
766	box = gtk_hbox_new(FALSE, 3);
767	gtk_container_add(GTK_CONTAINER(frame), box);
768
769	for (i = 0; i < NR_LABELS; i++)
770		types[i] = G_TYPE_STRING;
771
772	model = gtk_list_store_newv(NR_LABELS, types);
773
774	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
775	gtk_widget_set_can_focus(tree_view, FALSE);
776
777	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
778		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
779
780	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
781	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
782
783	for (i = 0; i < NR_LABELS; i++)
784		tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
785
786	gfio_add_total_depths_tree(model, ts, NR_LABELS);
787	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
788	gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
789
790	gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
791}
792
793static gboolean results_window_delete(GtkWidget *w, gpointer data)
794{
795	struct gui *ui = (struct gui *) data;
796
797	gtk_widget_destroy(w);
798	ui->results_window = NULL;
799	ui->results_notebook = NULL;
800	return TRUE;
801}
802
803static GtkWidget *get_results_window(struct gui *ui)
804{
805	GtkWidget *win, *notebook;
806
807	if (ui->results_window)
808		return ui->results_notebook;
809
810	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
811	gtk_window_set_title(GTK_WINDOW(win), "Results");
812	g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
813	g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
814
815	notebook = gtk_notebook_new();
816	gtk_container_add(GTK_CONTAINER(win), notebook);
817
818	ui->results_window = win;
819	ui->results_notebook = notebook;
820	return ui->results_notebook;
821}
822
823static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
824			    struct group_run_stats *rs)
825{
826	GtkWidget *res_win, *box, *vbox, *entry;
827	struct gfio_client *gc = client->client_data;
828
829	gdk_threads_enter();
830
831	res_win = get_results_window(gc->ui);
832
833	vbox = gtk_vbox_new(FALSE, 3);
834
835	box = gtk_hbox_new(TRUE, 3);
836	gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
837
838	gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
839
840	gc->results_widget = vbox;
841
842	entry = new_info_entry_in_frame(box, "Name");
843	gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
844	if (strlen(ts->description)) {
845		entry = new_info_entry_in_frame(box, "Description");
846		gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
847	}
848	entry = new_info_entry_in_frame(box, "Group ID");
849	entry_set_int_value(entry, ts->groupid);
850	entry = new_info_entry_in_frame(box, "Jobs");
851	entry_set_int_value(entry, ts->members);
852	entry = new_info_entry_in_frame(box, "Error");
853	entry_set_int_value(entry, ts->error);
854	entry = new_info_entry_in_frame(box, "PID");
855	entry_set_int_value(entry, ts->pid);
856
857	if (ts->io_bytes[DDIR_READ])
858		gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
859	if (ts->io_bytes[DDIR_WRITE])
860		gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
861
862	gfio_show_latency_buckets(vbox, ts);
863	gfio_show_cpu_usage(vbox, ts);
864	gfio_show_io_depths(vbox, ts);
865
866	gtk_widget_show_all(gc->ui->results_window);
867	gdk_threads_leave();
868}
869
870static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
871{
872	struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
873	struct gfio_client *gc = client->client_data;
874	GtkTreeIter iter;
875	struct tm *tm;
876	time_t sec;
877	char tmp[64], timebuf[80];
878
879	sec = p->log_sec;
880	tm = localtime(&sec);
881	strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
882	sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
883
884	gdk_threads_enter();
885
886	gtk_list_store_append(gc->ui->log_model, &iter);
887	gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
888	gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
889	gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
890	gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
891
892	gdk_threads_leave();
893}
894
895static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
896{
897	struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
898	struct gfio_client *gc = client->client_data;
899	GtkWidget *box, *frame, *entry, *vbox;
900
901	gdk_threads_enter();
902
903	if (!gc->results_widget) {
904		printf("no results!\n");
905		goto out;
906	}
907
908	if (!gc->disk_util_frame) {
909		gc->disk_util_frame = gtk_frame_new("Disk utilization");
910		gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
911	}
912
913	vbox = gtk_vbox_new(FALSE, 3);
914	gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
915
916	frame = gtk_frame_new((char *) p->dus.name);
917	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
918
919	box = gtk_vbox_new(FALSE, 3);
920	gtk_container_add(GTK_CONTAINER(frame), box);
921
922	frame = gtk_frame_new("Read");
923	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
924	vbox = gtk_hbox_new(TRUE, 3);
925	gtk_container_add(GTK_CONTAINER(frame), vbox);
926	entry = new_info_entry_in_frame(vbox, "IOs");
927	entry_set_int_value(entry, p->dus.ios[0]);
928	entry = new_info_entry_in_frame(vbox, "Merges");
929	entry_set_int_value(entry, p->dus.merges[0]);
930	entry = new_info_entry_in_frame(vbox, "Sectors");
931	entry_set_int_value(entry, p->dus.sectors[0]);
932	entry = new_info_entry_in_frame(vbox, "Ticks");
933	entry_set_int_value(entry, p->dus.ticks[0]);
934
935	frame = gtk_frame_new("Write");
936	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
937	vbox = gtk_hbox_new(TRUE, 3);
938	gtk_container_add(GTK_CONTAINER(frame), vbox);
939	entry = new_info_entry_in_frame(vbox, "IOs");
940	entry_set_int_value(entry, p->dus.ios[1]);
941	entry = new_info_entry_in_frame(vbox, "Merges");
942	entry_set_int_value(entry, p->dus.merges[1]);
943	entry = new_info_entry_in_frame(vbox, "Sectors");
944	entry_set_int_value(entry, p->dus.sectors[1]);
945	entry = new_info_entry_in_frame(vbox, "Ticks");
946	entry_set_int_value(entry, p->dus.ticks[1]);
947
948	frame = gtk_frame_new("Shared");
949	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
950	vbox = gtk_hbox_new(TRUE, 3);
951	gtk_container_add(GTK_CONTAINER(frame), vbox);
952	entry = new_info_entry_in_frame(vbox, "IO ticks");
953	entry_set_int_value(entry, p->dus.io_ticks);
954	entry = new_info_entry_in_frame(vbox, "Time in queue");
955	entry_set_int_value(entry, p->dus.time_in_queue);
956
957	gtk_widget_show_all(gc->results_widget);
958out:
959	gdk_threads_leave();
960}
961
962extern int sum_stat_clients;
963extern struct thread_stat client_ts;
964extern struct group_run_stats client_gs;
965
966static int sum_stat_nr;
967
968static void gfio_thread_status_op(struct fio_client *client,
969				  struct fio_net_cmd *cmd)
970{
971	struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
972
973	gfio_display_ts(client, &p->ts, &p->rs);
974
975	if (sum_stat_clients == 1)
976		return;
977
978	sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
979	sum_group_stats(&client_gs, &p->rs);
980
981	client_ts.members++;
982	client_ts.groupid = p->ts.groupid;
983
984	if (++sum_stat_nr == sum_stat_clients) {
985		strcpy(client_ts.name, "All clients");
986		gfio_display_ts(client, &client_ts, &client_gs);
987	}
988}
989
990static void gfio_group_stats_op(struct fio_client *client,
991				struct fio_net_cmd *cmd)
992{
993	gdk_threads_enter();
994	printf("gfio_group_stats_op called\n");
995	fio_client_ops.group_stats(client, cmd);
996	gdk_threads_leave();
997}
998
999static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1000{
1001	struct gui *ui = (struct gui *) p;
1002	cairo_t *cr;
1003
1004	cr = gdk_cairo_create(w->window);
1005
1006	cairo_set_source_rgb(cr, 0, 0, 0);
1007
1008	cairo_save(cr);
1009	cairo_translate(cr, 0, 0);
1010	line_graph_draw(ui->bandwidth_graph, cr);
1011	cairo_stroke(cr);
1012	cairo_restore(cr);
1013
1014	cairo_save(cr);
1015	cairo_translate(cr, DRAWING_AREA_XDIM / 2.0, 0);
1016				// DRAWING_AREA_YDIM * 0.05);
1017	line_graph_draw(ui->iops_graph, cr);
1018	cairo_stroke(cr);
1019	cairo_restore(cr);
1020	cairo_destroy(cr);
1021
1022	return FALSE;
1023}
1024
1025static void gfio_update_eta(struct jobs_eta *je)
1026{
1027	static int eta_good;
1028	char eta_str[128];
1029	char output[256];
1030	char tmp[32];
1031	double perc = 0.0;
1032	int i2p = 0;
1033
1034	gdk_threads_enter();
1035
1036	eta_str[0] = '\0';
1037	output[0] = '\0';
1038
1039	if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1040		perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1041		eta_to_str(eta_str, je->eta_sec);
1042	}
1043
1044	sprintf(tmp, "%u", je->nr_running);
1045	gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
1046	sprintf(tmp, "%u", je->files_open);
1047	gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
1048
1049#if 0
1050	if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1051	if (je->m_rate || je->t_rate) {
1052		char *tr, *mr;
1053
1054		mr = num2str(je->m_rate, 4, 0, i2p);
1055		tr = num2str(je->t_rate, 4, 0, i2p);
1056		gtk_entry_set_text(GTK_ENTRY(ui.eta);
1057		p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1058		free(tr);
1059		free(mr);
1060	} else if (je->m_iops || je->t_iops)
1061		p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1062
1063	gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1064	gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1065	gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1066	gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1067#endif
1068
1069	if (je->eta_sec != INT_MAX && je->nr_running) {
1070		char *iops_str[2];
1071		char *rate_str[2];
1072
1073		if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1074			strcpy(output, "-.-% done");
1075		else {
1076			eta_good = 1;
1077			perc *= 100.0;
1078			sprintf(output, "%3.1f%% done", perc);
1079		}
1080
1081		rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1082		rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1083
1084		iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1085		iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1086
1087		gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1088		gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1089		gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1090		gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1091
1092		graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1093		graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1094		graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1095		graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1096
1097		free(rate_str[0]);
1098		free(rate_str[1]);
1099		free(iops_str[0]);
1100		free(iops_str[1]);
1101	}
1102
1103	if (eta_str[0]) {
1104		char *dst = output + strlen(output);
1105
1106		sprintf(dst, " - %s", eta_str);
1107	}
1108
1109	gfio_update_thread_status(output, perc);
1110	gdk_threads_leave();
1111}
1112
1113static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1114{
1115	struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1116	struct gfio_client *gc = client->client_data;
1117	struct gui *ui = gc->ui;
1118	const char *os, *arch;
1119	char buf[64];
1120
1121	os = fio_get_os_string(probe->os);
1122	if (!os)
1123		os = "unknown";
1124
1125	arch = fio_get_arch_string(probe->arch);
1126	if (!arch)
1127		os = "unknown";
1128
1129	if (!client->name)
1130		client->name = strdup((char *) probe->hostname);
1131
1132	gdk_threads_enter();
1133
1134	gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1135	gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1136	gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1137	sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1138	gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1139
1140	gfio_set_connected(ui, 1);
1141
1142	gdk_threads_leave();
1143}
1144
1145static void gfio_update_thread_status(char *status_message, double perc)
1146{
1147	static char message[100];
1148	const char *m = message;
1149
1150	strncpy(message, status_message, sizeof(message) - 1);
1151	gtk_progress_bar_set_text(
1152		GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1153	gtk_progress_bar_set_fraction(
1154		GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1155	gtk_widget_queue_draw(ui.window);
1156}
1157
1158static void gfio_quit_op(struct fio_client *client)
1159{
1160	struct gfio_client *gc = client->client_data;
1161
1162	gdk_threads_enter();
1163	gfio_set_connected(gc->ui, 0);
1164	gdk_threads_leave();
1165}
1166
1167static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1168{
1169	struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1170	struct gfio_client *gc = client->client_data;
1171	struct gui *ui = gc->ui;
1172	char tmp[8];
1173	int i;
1174
1175	p->iodepth		= le32_to_cpu(p->iodepth);
1176	p->rw			= le32_to_cpu(p->rw);
1177
1178	for (i = 0; i < 2; i++) {
1179		p->min_bs[i] 	= le32_to_cpu(p->min_bs[i]);
1180		p->max_bs[i]	= le32_to_cpu(p->max_bs[i]);
1181	}
1182
1183	p->numjobs		= le32_to_cpu(p->numjobs);
1184	p->group_reporting	= le32_to_cpu(p->group_reporting);
1185
1186	gdk_threads_enter();
1187
1188	gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1189	gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1190	gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1191
1192	sprintf(tmp, "%u", p->iodepth);
1193	gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1194
1195	gdk_threads_leave();
1196}
1197
1198static void gfio_client_timed_out(struct fio_client *client)
1199{
1200	struct gfio_client *gc = client->client_data;
1201	GtkWidget *dialog, *label, *content;
1202	char buf[256];
1203
1204	gdk_threads_enter();
1205
1206	gfio_set_connected(gc->ui, 0);
1207	clear_ui_info(gc->ui);
1208
1209	sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1210
1211	dialog = gtk_dialog_new_with_buttons("Timed out!",
1212			GTK_WINDOW(gc->ui->window),
1213			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1214			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1215
1216	content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1217	label = gtk_label_new((const gchar *) buf);
1218	gtk_container_add(GTK_CONTAINER(content), label);
1219	gtk_widget_show_all(dialog);
1220	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1221
1222	gtk_dialog_run(GTK_DIALOG(dialog));
1223	gtk_widget_destroy(dialog);
1224
1225	gdk_threads_leave();
1226}
1227
1228struct client_ops gfio_client_ops = {
1229	.text_op		= gfio_text_op,
1230	.disk_util		= gfio_disk_util_op,
1231	.thread_status		= gfio_thread_status_op,
1232	.group_stats		= gfio_group_stats_op,
1233	.eta			= gfio_update_eta,
1234	.probe			= gfio_probe_op,
1235	.quit			= gfio_quit_op,
1236	.add_job		= gfio_add_job_op,
1237	.timed_out		= gfio_client_timed_out,
1238	.stay_connected		= 1,
1239};
1240
1241static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1242                __attribute__((unused)) gpointer data)
1243{
1244        gtk_main_quit();
1245}
1246
1247static void *job_thread(void *arg)
1248{
1249	fio_handle_clients(&gfio_client_ops);
1250	return NULL;
1251}
1252
1253static int send_job_files(struct gui *ui)
1254{
1255	int i, ret = 0;
1256
1257	for (i = 0; i < ui->nr_job_files; i++) {
1258		ret = fio_clients_send_ini(ui->job_files[i]);
1259		if (ret)
1260			break;
1261
1262		free(ui->job_files[i]);
1263		ui->job_files[i] = NULL;
1264	}
1265	while (i < ui->nr_job_files) {
1266		free(ui->job_files[i]);
1267		ui->job_files[i] = NULL;
1268		i++;
1269	}
1270
1271	return ret;
1272}
1273
1274static void start_job_thread(struct gui *ui)
1275{
1276	if (send_job_files(ui)) {
1277		printf("Yeah, I didn't really like those options too much.\n");
1278		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1279		return;
1280	}
1281}
1282
1283static void *server_thread(void *arg)
1284{
1285	is_backend = 1;
1286	gfio_server_running = 1;
1287	fio_start_server(NULL);
1288	gfio_server_running = 0;
1289	return NULL;
1290}
1291
1292static void gfio_start_server(struct gui *ui)
1293{
1294	if (!gfio_server_running) {
1295		gfio_server_running = 1;
1296		pthread_create(&ui->server_t, NULL, server_thread, NULL);
1297		pthread_detach(ui->server_t);
1298	}
1299}
1300
1301static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1302                gpointer data)
1303{
1304	struct gui *ui = data;
1305
1306	gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1307	start_job_thread(ui);
1308}
1309
1310static void file_open(GtkWidget *w, gpointer data);
1311
1312static void connect_clicked(GtkWidget *widget, gpointer data)
1313{
1314	struct gui *ui = data;
1315
1316	if (!ui->connected) {
1317		if (!ui->nr_job_files)
1318			file_open(widget, data);
1319		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1320		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1321		if (!fio_clients_connect()) {
1322			pthread_create(&ui->t, NULL, job_thread, NULL);
1323			gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1324		}
1325	} else {
1326		fio_clients_terminate();
1327		gfio_set_connected(ui, 0);
1328		clear_ui_info(ui);
1329	}
1330}
1331
1332static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1333			struct button_spec *buttonspec)
1334{
1335	ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1336	g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1337	gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1338	gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1339	gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1340}
1341
1342static void add_buttons(struct gui *ui,
1343				struct button_spec *buttonlist,
1344				int nbuttons)
1345{
1346	int i;
1347
1348	for (i = 0; i < nbuttons; i++)
1349		add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1350}
1351
1352static void on_info_bar_response(GtkWidget *widget, gint response,
1353                                 gpointer data)
1354{
1355	if (response == GTK_RESPONSE_OK) {
1356		gtk_widget_destroy(widget);
1357		ui.error_info_bar = NULL;
1358	}
1359}
1360
1361void report_error(GError *error)
1362{
1363	if (ui.error_info_bar == NULL) {
1364		ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1365		                                               GTK_RESPONSE_OK,
1366		                                               NULL);
1367		g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1368		gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1369		                              GTK_MESSAGE_ERROR);
1370
1371		ui.error_label = gtk_label_new(error->message);
1372		GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1373		gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1374
1375		gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1376		gtk_widget_show_all(ui.vbox);
1377	} else {
1378		char buffer[256];
1379		snprintf(buffer, sizeof(buffer), "Failed to open file.");
1380		gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1381	}
1382}
1383
1384static int get_connection_details(char **host, int *port, int *type,
1385				  int *server_start)
1386{
1387	GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1388	GtkWidget *button;
1389	char *typeentry;
1390
1391	dialog = gtk_dialog_new_with_buttons("Connection details",
1392			GTK_WINDOW(ui.window),
1393			GTK_DIALOG_DESTROY_WITH_PARENT,
1394			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1395			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1396
1397	frame = gtk_frame_new("Hostname / socket name");
1398	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1399	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1400
1401	box = gtk_vbox_new(FALSE, 6);
1402	gtk_container_add(GTK_CONTAINER(frame), box);
1403
1404	hbox = gtk_hbox_new(TRUE, 10);
1405	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1406	hentry = gtk_entry_new();
1407	gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1408	gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1409
1410	frame = gtk_frame_new("Port");
1411	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1412	box = gtk_vbox_new(FALSE, 10);
1413	gtk_container_add(GTK_CONTAINER(frame), box);
1414
1415	hbox = gtk_hbox_new(TRUE, 4);
1416	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1417	pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1418
1419	frame = gtk_frame_new("Type");
1420	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1421	box = gtk_vbox_new(FALSE, 10);
1422	gtk_container_add(GTK_CONTAINER(frame), box);
1423
1424	hbox = gtk_hbox_new(TRUE, 4);
1425	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1426
1427	combo = gtk_combo_box_new_text();
1428	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1429	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1430	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1431	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1432
1433	gtk_container_add(GTK_CONTAINER(hbox), combo);
1434
1435	frame = gtk_frame_new("Options");
1436	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1437	box = gtk_vbox_new(FALSE, 10);
1438	gtk_container_add(GTK_CONTAINER(frame), box);
1439
1440	hbox = gtk_hbox_new(TRUE, 4);
1441	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1442
1443	button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1444	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1445	gtk_widget_set_tooltip_text(button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
1446	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1447
1448	gtk_widget_show_all(dialog);
1449
1450	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1451		gtk_widget_destroy(dialog);
1452		return 1;
1453	}
1454
1455	*host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1456	*port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1457
1458	typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1459	if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1460		*type = Fio_client_ipv4;
1461	else if (!strncmp(typeentry, "IPv6", 4))
1462		*type = Fio_client_ipv6;
1463	else
1464		*type = Fio_client_socket;
1465	g_free(typeentry);
1466
1467	*server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1468
1469	gtk_widget_destroy(dialog);
1470	return 0;
1471}
1472
1473static void gfio_client_added(struct gui *ui, struct fio_client *client)
1474{
1475	struct gfio_client *gc;
1476
1477	gc = malloc(sizeof(*gc));
1478	memset(gc, 0, sizeof(*gc));
1479	gc->ui = ui;
1480
1481	client->client_data = gc;
1482}
1483
1484static void file_open(GtkWidget *w, gpointer data)
1485{
1486	GtkWidget *dialog;
1487	struct gui *ui = data;
1488	GSList *filenames, *fn_glist;
1489	GtkFileFilter *filter;
1490	char *host;
1491	int port, type, server_start;
1492
1493	dialog = gtk_file_chooser_dialog_new("Open File",
1494		GTK_WINDOW(ui->window),
1495		GTK_FILE_CHOOSER_ACTION_OPEN,
1496		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1497		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1498		NULL);
1499	gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1500
1501	filter = gtk_file_filter_new();
1502	gtk_file_filter_add_pattern(filter, "*.fio");
1503	gtk_file_filter_add_pattern(filter, "*.job");
1504	gtk_file_filter_add_pattern(filter, "*.ini");
1505	gtk_file_filter_add_mime_type(filter, "text/fio");
1506	gtk_file_filter_set_name(filter, "Fio job file");
1507	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1508
1509	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1510		gtk_widget_destroy(dialog);
1511		return;
1512	}
1513
1514	fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1515
1516	gtk_widget_destroy(dialog);
1517
1518	if (get_connection_details(&host, &port, &type, &server_start))
1519		goto err;
1520
1521	filenames = fn_glist;
1522	while (filenames != NULL) {
1523		struct fio_client *client;
1524
1525		ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1526		ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1527		ui->nr_job_files++;
1528
1529		client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1530		if (!client) {
1531			GError *error;
1532
1533			error = g_error_new(g_quark_from_string("fio"), 1,
1534					"Failed to add client %s", host);
1535			report_error(error);
1536			g_error_free(error);
1537		}
1538		gfio_client_added(ui, client);
1539
1540		g_free(filenames->data);
1541		filenames = g_slist_next(filenames);
1542	}
1543	free(host);
1544
1545	if (server_start)
1546		gfio_start_server(ui);
1547err:
1548	g_slist_free(fn_glist);
1549}
1550
1551static void file_save(GtkWidget *w, gpointer data)
1552{
1553	struct gui *ui = data;
1554	GtkWidget *dialog;
1555
1556	dialog = gtk_file_chooser_dialog_new("Save File",
1557		GTK_WINDOW(ui->window),
1558		GTK_FILE_CHOOSER_ACTION_SAVE,
1559		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1560		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1561		NULL);
1562
1563	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1564	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1565
1566	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1567		char *filename;
1568
1569		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1570		// save_job_file(filename);
1571		g_free(filename);
1572	}
1573	gtk_widget_destroy(dialog);
1574}
1575
1576static void view_log_destroy(GtkWidget *w, gpointer data)
1577{
1578	struct gui *ui = (struct gui *) data;
1579
1580	gtk_widget_ref(ui->log_tree);
1581	gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1582	gtk_widget_destroy(w);
1583	ui->log_view = NULL;
1584}
1585
1586static void view_log(GtkWidget *w, gpointer data)
1587{
1588	GtkWidget *win, *scroll, *vbox, *box;
1589	struct gui *ui = (struct gui *) data;
1590
1591	if (ui->log_view)
1592		return;
1593
1594	ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1595	gtk_window_set_title(GTK_WINDOW(win), "Log");
1596	gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1597
1598	scroll = gtk_scrolled_window_new(NULL, NULL);
1599
1600	gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1601
1602	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1603
1604	box = gtk_hbox_new(TRUE, 0);
1605	gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1606	g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1607	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1608
1609	vbox = gtk_vbox_new(TRUE, 5);
1610	gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1611
1612	gtk_container_add(GTK_CONTAINER(win), vbox);
1613	gtk_widget_show_all(win);
1614}
1615
1616static void preferences(GtkWidget *w, gpointer data)
1617{
1618	GtkWidget *dialog, *frame, *box, **buttons;
1619	int i;
1620
1621	dialog = gtk_dialog_new_with_buttons("Preferences",
1622		GTK_WINDOW(ui.window),
1623		GTK_DIALOG_DESTROY_WITH_PARENT,
1624		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1625		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1626		NULL);
1627
1628	frame = gtk_frame_new("Debug logging");
1629	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1630	box = gtk_hbox_new(FALSE, 6);
1631	gtk_container_add(GTK_CONTAINER(frame), box);
1632
1633	buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1634
1635	for (i = 0; i < FD_DEBUG_MAX; i++) {
1636		buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1637		gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1638		gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1639	}
1640
1641	gtk_widget_show_all(dialog);
1642
1643	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1644		gtk_widget_destroy(dialog);
1645		return;
1646	}
1647
1648	for (i = 0; i < FD_DEBUG_MAX; i++) {
1649		int set;
1650
1651		set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1652		if (set)
1653			fio_debug |= (1UL << i);
1654	}
1655
1656	gtk_widget_destroy(dialog);
1657}
1658
1659static void about_dialog(GtkWidget *w, gpointer data)
1660{
1661	gtk_show_about_dialog(NULL,
1662		"program-name", "gfio",
1663		"comments", "Gtk2 UI for fio",
1664		"license", "GPLv2",
1665		"version", fio_version_string,
1666		"copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1667		"logo-icon-name", "fio",
1668		/* Must be last: */
1669		NULL, NULL,
1670		NULL);
1671}
1672
1673static GtkActionEntry menu_items[] = {
1674	{ "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1675	{ "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1676	{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1677	{ "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1678	{ "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1679	{ "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1680	{ "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1681	{ "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1682	{ "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1683};
1684static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1685
1686static const gchar *ui_string = " \
1687	<ui> \
1688		<menubar name=\"MainMenu\"> \
1689			<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1690				<menuitem name=\"Open\" action=\"OpenFile\" /> \
1691				<menuitem name=\"Save\" action=\"SaveFile\" /> \
1692				<separator name=\"Separator\"/> \
1693				<menuitem name=\"Preferences\" action=\"Preferences\" /> \
1694				<separator name=\"Separator2\"/> \
1695				<menuitem name=\"Quit\" action=\"Quit\" /> \
1696			</menu> \
1697			<menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1698				<menuitem name=\"Log\" action=\"ViewLog\" /> \
1699			</menu>\
1700			<menu name=\"Help\" action=\"HelpMenuAction\"> \
1701				<menuitem name=\"About\" action=\"About\" /> \
1702			</menu> \
1703		</menubar> \
1704	</ui> \
1705";
1706
1707static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1708				   struct gui *ui)
1709{
1710	GtkActionGroup *action_group = gtk_action_group_new("Menu");
1711	GError *error = 0;
1712
1713	action_group = gtk_action_group_new("Menu");
1714	gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1715
1716	gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1717	gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1718
1719	gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1720	return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1721}
1722
1723void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1724                   GtkWidget *vbox, GtkUIManager *ui_manager)
1725{
1726        gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1727}
1728
1729static void init_ui(int *argc, char **argv[], struct gui *ui)
1730{
1731	GtkSettings *settings;
1732	GtkUIManager *uimanager;
1733	GtkWidget *menu, *probe, *probe_frame, *probe_box;
1734
1735	memset(ui, 0, sizeof(*ui));
1736
1737	/* Magical g*thread incantation, you just need this thread stuff.
1738	 * Without it, the update that happens in gfio_update_thread_status
1739	 * doesn't really happen in a timely fashion, you need expose events
1740	 */
1741	if (!g_thread_supported())
1742		g_thread_init(NULL);
1743	gdk_threads_init();
1744
1745	gtk_init(argc, argv);
1746	settings = gtk_settings_get_default();
1747	gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1748	g_type_init();
1749
1750	ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1751        gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1752	gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1753
1754	g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1755	g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1756
1757	ui->vbox = gtk_vbox_new(FALSE, 0);
1758	gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1759
1760	uimanager = gtk_ui_manager_new();
1761	menu = get_menubar_menu(ui->window, uimanager, ui);
1762	gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1763
1764	/*
1765	 * Set up alignments for widgets at the top of ui,
1766	 * align top left, expand horizontally but not vertically
1767	 */
1768	ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1769	ui->topvbox = gtk_vbox_new(FALSE, 3);
1770	gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1771	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1772
1773	probe = gtk_frame_new("Job");
1774	gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1775	probe_frame = gtk_vbox_new(FALSE, 3);
1776	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1777
1778	probe_box = gtk_hbox_new(FALSE, 3);
1779	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1780	ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1781	ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1782	ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1783	ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1784
1785	probe_box = gtk_hbox_new(FALSE, 3);
1786	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1787
1788	ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1789	ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1790	ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1791	ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1792	ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1793	ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1794
1795	probe_box = gtk_hbox_new(FALSE, 3);
1796	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1797	ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1798	ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1799	ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1800	ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1801
1802	/*
1803	 * Only add this if we have a commit rate
1804	 */
1805#if 0
1806	probe_box = gtk_hbox_new(FALSE, 3);
1807	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1808
1809	ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1810	ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1811
1812	ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1813	ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1814#endif
1815
1816	/*
1817	 * Set up a drawing area and IOPS and bandwidth graphs
1818	 */
1819	ui->drawing_area = gtk_drawing_area_new();
1820	gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area),
1821			DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1822	g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
1823				G_CALLBACK (on_expose_drawing_area), ui);
1824	ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1825	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1826					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1827	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1828					ui->drawing_area);
1829	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1830			TRUE, TRUE, 0);
1831
1832	setup_iops_graph(ui);
1833	setup_bandwidth_graph(ui);
1834
1835	/*
1836	 * Set up alignments for widgets at the bottom of ui,
1837	 * align bottom left, expand horizontally but not vertically
1838	 */
1839	ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1840	ui->buttonbox = gtk_hbox_new(FALSE, 0);
1841	gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1842	gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1843					FALSE, FALSE, 0);
1844
1845	add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1846
1847	/*
1848	 * Set up thread status progress bar
1849	 */
1850	ui->thread_status_pb = gtk_progress_bar_new();
1851	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1852	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1853	gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1854
1855	gfio_ui_setup_log(ui);
1856
1857	gtk_widget_show_all(ui->window);
1858}
1859
1860int main(int argc, char *argv[], char *envp[])
1861{
1862	if (initialize_fio(envp))
1863		return 1;
1864	if (fio_init_options())
1865		return 1;
1866
1867	init_ui(&argc, &argv, &ui);
1868
1869	gdk_threads_enter();
1870	gtk_main();
1871	gdk_threads_leave();
1872	return 0;
1873}
1874