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